Linux 多线程

来源:互联网 发布:个人职业规划 知乎 编辑:程序博客网 时间:2024/06/05 01:10

目录:

  1. 线程种类
  2. 线程的创建
  3. 对线程的管理
  4. 线程的属性
  5. 线程之间的互斥

1、线程种类

内核态线程
(1)由内核调度程序直接调度,充分发挥多处理器的优势
(2)目前linux系统标准线程库采用内核线程方式实现多线程
用户态线程
(1)一个进程包含多个线程,这些线程从内核调度角度来看只是一个进程,内核把它当一个进程来调度。线程之间调度在用户态进行
(2)用户态线程优点是调度效率高(不需要内核参与调度),缺点是对于多核处理器利用率不高,一个线程阻塞会导致整个线程组阻塞
区分内核态线程与用户态线程
如果CPU为4核心,此时内核态线程有4个,那么CPU的4个核心分别执行一个线程,CPU的利用率最高,执行效率也最快,但是开始运行的时候线程会从用户态切换到内核态,运行完成线程后又从从内核态切换到用户态,这会消耗其他的资源,浪费时间。
如果此时用户态线程有4个,运行时操作系统只会给这个进程分配一个CPU核心数(进程是资源分配的最小单位),此时进程内的4个线程共用这一个CPU核心数,在进程内它们根据优先级的大小来轮流抢占这个进程的时间片实现“同步”,微观上讲是有间隔的,内核线程才是真正的同步,每个线程都运行在自己的CPU核心上,不用抢占时间片。由于用户态线程是运行在进程内部的,所以操作系统是不知道用户态线程的,它只知道这一个进程(这与线程是CPU调度的最小单位不矛盾)。

2、线程的创建

pthread_create

#include<pthread.h>int  pthread_create(pthread_t *id,  pthread_attr_t  *attr,  void *pFun,  void *args)

• id : 返回线程ID
• attr: 线程属性,无属性设置填NULL
• pFun: 线程调用的函数
• args: 线程函数的参数
Demo

#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <unistd.h>#include <string.h>void* ThreadFunc(void* param){    int n = *((int*)param);    int iRet = 0;    int i;    for (i = 0; i < n; i++)    {        iRet += i;        fprintf(stderr, "thread[%d]->%d\n", n, iRet);        usleep(100);    }    printf("\n");    return NULL;}int main(){    pthread_t thread_id;    int num = 5;    pthread_create(&thread_id, NULL, ThreadFunc, (void*)&num);    pthread_join(thread_id, NULL);    return 0;}

3、对线程的管理

Function

pthread_self()

功能:线程函数里,获取本线程的线程ID

int  pthread_join(pthread_t  id,  void **pRet)

功能:使主进程等待线程完成后才退出
pRet: 获得 pthread_exit 函数调用的返回值

pthread_exit(void *pRet)

pRet:指定线程退出返回值
Demo

#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <unistd.h>#include <string.h>int g_pRet;void* thread(){    g_pRet = 100;    pthread_exit(&g_pRet);    return NULL;}int main(){    pthread_t tId;    int iRet;    iRet = pthread_create(&tId, NULL, thread, NULL);    if (iRet)    {        perror("Fail to pthread_create!");        return iRet;    }    int *pRet;    pthread_join(tId, (void*)&pRet);    printf("thread exit code:pRet = %d\n", *pRet);    return 0;}

4、线程的属性

线程属性结构体

struct pthread_attr_t {    int __detachstate;    int __schedpolicy;    struct sched_param  __schedparam;    int __inheritsched;    int __scope;          size_t __guardsize;    int __stackaddr_set;    void * __stackaddr;    unsigned long int __stacksize;

__detachstate:可分离属性(是否与进程中其他线程脱离同步)
__schedpolicy:调度策略
__schedparam:线程优先级
__inheritsched:继承属性
__scope:线程种类(内核态、用户态)
__guardsize:警戒缓冲区,防止桟溢出的扩展内存大小(防止一定程度的缓冲区溢出造成的程序崩溃)
__stackaddr_set:
__stackaddr:堆栈地址
__stacksize:堆栈的大小


Function

pthread_getattr_np(pthread_t id, pthread_attr_t *attr)

功能:获取线程属性

pthread_attr_init(pthread_attr_t  *attr)

功能:初始化线程属性(初始化的为默认属性)

int  pthread_attr_setdetachstate(pthread_attr_t *attr,  int  detachstate)int  pthread_attr_getdetachstate(pthread_attr_t *attr,  int *pdetachstate)

功能:设置/获取线程分离状态
• PTHREAD_CREATE_JOINABLE 线程可与其他线程同步(默认),此时pthread_join()才会生效
• PTHREAD_CREATE_DETACHED 线程的资源在退出后自行释放

Demo

#define _GNU_SOURCE#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <unistd.h>void* ThreadFunc(void* param){    pthread_attr_t attr;    // 线程函数里,获取本线程的线程ID    pthread_t thread_id = pthread_self();    // 获取线程属性放在attr中    pthread_getattr_np(thread_id, &attr);    int iDetachstate;    // 从attr中获取detach属性    pthread_attr_getdetachstate(&attr, &iDetachstate);    printf("detachstate:%s\n", PTHREAD_CREATE_JOINABLE==iDetachstate?"Joinble":"Detached");    return NULL;}int main(){    //Demo_ThreadAttr();    pthread_attr_t attr;    // 初始化线程属性,初始化的为默认值,只有设置才能改变    pthread_attr_init(&attr);    // 设置线程分离状态    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);    pthread_t thread_id;    pthread_create(&thread_id, &attr, (void*)ThreadFunc, NULL);    usleep(1000);    return 0;}

其他属性

int pthread_attr_setschedpolicy(pthread_attr_t *attr,  int  policy)int pthread_attr_getschedpolicy (pthread_attr_t *attr,  int *pPolicy)

功能:设置/获取线程调度策略
• SCHED_OTHER 系统默认(分时调度),各个优先级任务时间轮换
• SCHED_FIFO 实时调度,先到先服务(独占)
• SCHED_RR 实时调度,时间片轮转(高优先级轮换)

int pthread_attr_setinheritsched(pthread_attr_t *attr,  int  inheritsched)int pthread_attr_getinheritsched(pthread_attr_t *attr,  int  *pinheritsched)

功能:设置/获取线程继承性
• PTHREAD_INHERIT_SCHED 从父进程继承调度属性
• PTHREAD_EXPLICIT_SCHED 不从父进程继承调度属性

int pthread_attr_setscope(pthread_attr_t *attr,  int scope)int pthread_attr_getscope(pthread_attr_t *attr,  int *pscope)

功能:设置/获取线程作用域
• PTHREAD_SCOPE_SYSTEM 系统所有进程间调度
• PTHREAD_SCOPE_PROCESS 当前进程间调度

int pthread_attr_setstackaddr(pthread_attr_t *attr,  void * stackaddr)int pthread_attr_getstackaddr(pthread_attr_t *attr,  void ** stackaddr)int pthread_attr_setstacksize(pthread_attr_t *attr,  size_t  *stacksize)int pthread_attr_setstacksize(pthread_attr_t *attr,  size_t  *stacksize)

功能:获取/设置线程桟信息

int pthread_attr_getguardsize(pthread_attr_t *attr, size_t  *guardsize)int pthread_attr_setguardsize(pthread_attr_t *attr, size_t  *guardsize)

功能:设置/获取线程警戒缓冲区

pthread_attr_destroy(pthread_attr_t *attr)

功能:无效化线程属性,无效化后, 使用attr 创建线程会失败

5、线程之间的互斥

多线程共享进程资源,访问共享资源时,需要进行互斥操作确保数据的有效性。此处讲解的为互斥锁实现线程的同步互斥。
互斥锁类型
– mutex 锁类型->PTHREAD_MUTEX_XXX
NORMAL: 普通锁,不提供死锁检测,可能出现死锁
ERRORCHECK: 检错锁,同一线程对已锁的互斥锁加锁会返回错误
RECURSIVE: 嵌套锁,允许同一线程多次锁定而不产生死锁,多次释放才能被别的线程锁定
DEFAULT: 默认为普通锁,排队获取锁
设置/获取锁类型

int pthread_mutexattr_settype(pthread_mutexattr_t*  attr, int type);int pthread_mutexattr_gettype(pthread_mutexattr_t*  attr, int *type);

线程互斥方法

–   创建互斥锁pthread_mutex_init(pthread_mutex_t  *mutex, pthread_mutexattr_t  *attr)–   互斥锁加锁pthread_mutex_lock(pthread_mutex_t  *mutex)–   互斥锁解锁pthread_mutex_unlock(pthread_mutex_t  *mutex)

Demo

#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <unistd.h>#include <string.h>pthread_mutex_t lock;// 声明一个互斥锁int g_num;void* ThreadFunc_lock1(void* param){    int iRet;    while (g_num > 0)    {        iRet = pthread_mutex_lock(&lock);// 加锁,锁定这段资源(这里共享的g_num)        if (iRet)        {            printf("lock error\n");            usleep(1000);        }        printf("thread1:%d\n", g_num);        g_num--;        pthread_mutex_unlock(&lock);// 解锁        usleep(1);    }    return NULL;}void* ThreadFunc_lock2(void* param){    int iRet;    while (g_num > 0)    {        iRet = pthread_mutex_lock(&lock);// 加锁,锁定这段资源(这里共享的g_num)        if (iRet)        {            printf("lock error\n");            usleep(1000);        }        printf("thead2:%d\n", g_num);        g_num--;        pthread_mutex_unlock(&lock);// 解锁        usleep(1);    }    return NULL;}int main(){    //pthread_mutex_init(&lock, NULL);// 初始化为默认锁    pthread_mutexattr_t attr;    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);    // 等价上面两句 lock = PTHREAD_MUTEX_ERRORCHECK;    pthread_mutex_init(&lock, &attr);    pthread_t tid1, tid2;    g_num = 100;    pthread_create(&tid1, NULL, ThreadFunc_lock1, NULL);    pthread_create(&tid2, NULL, ThreadFunc_lock2, NULL);    pthread_join(tid1, NULL);    pthread_join(tid2, NULL);    return 0;}

所有示例的makefile

.PHONY:clean allSRC=$(wildcard *.c)    # 找出所有当前目录的.c文件OBJ=$(SRC:%.c=%.o) # 把SRC所有的.c文件赋值给.o文件BIN=$(OBJ:%.o=%)   # 去掉所有的.oCC=gcc          # CC默认表示gccCFLAGS=-g -Wall # gcc 编译参数-g -WallLIBS=-lpthread      # 依赖的库目录# $<: 依赖的第一个文件# $^:所有的依赖文件# $@:就是目标文件%# 生成的目标文件是all,目标文件依赖所有的BIN文件# 此处使用了makefile的自动推导功能,OBJ是.o文件,makefile就会去找怎么生成.o文件all:clean  $(BIN) $(BIN):%:%.c    $(CC) $(CFLAGS) $< -o $@ $(LIBS)clean:    rm -rf $(BIN) $(OBJ)
原创粉丝点击