Linux 多线程

目录:

  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 all

SRC=$(wildcard *.c)    # 找出所有当前目录的.c文件
OBJ=$(SRC:%.c=%.o) # 把SRC所有的.c文件赋值给.o文件
BIN=$(OBJ:%.o=%)   # 去掉所有的.o

CC=gcc          # CC默认表示gcc
CFLAGS=-g -Wall # gcc 编译参数-g -Wall

LIBS=-lpthread      # 依赖的库目录
# $<: 依赖的第一个文件
# $^:所有的依赖文件
# $@:就是目标文件%
# 生成的目标文件是all,目标文件依赖所有的BIN文件
# 此处使用了makefile的自动推导功能,OBJ是.o文件,makefile就会去找怎么生成.o文件
all:clean  $(BIN) 

$(BIN):%:%.c
    $(CC) $(CFLAGS) $< -o $@ $(LIBS)

clean:
    rm -rf $(BIN) $(OBJ)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值