Linux线程

目录

线程概念

线程与进程的区别

为什么要使用线程

线程标识符

 实例

线程相关函数 

线程创建函数pthread_create()

线程等待函数pthread_join()

线程退出函数pthread_exit()

实例(不考虑线程退出状态的线程)

实例(考虑线程退出状态的线程) 

线程取消函数pthread_cancel()

登记线程退出函数pthread_cleanup_push() 

与pthread_cleanup_pop()成对出现函数phread_cleanup_pop()

实例 (线程2的循环体执行五次后请求线程1退出,线程1退出后执行登记函数)

互斥量

创建一把锁

初始化一把锁

静态初始化,普通赋值

动态初始化,使用函数pthread_mutex_init()

销毁一把锁pthread_mutex_destroy()

加锁

加锁pthread_mutex_lock()

尝试加锁pthread_mutex_trylock()

带倒计时的加锁pthread_mutex_timedlock()

解锁pthread_mutex_unlock()

实例(使用互斥量锁住共享资源,让一个时间段只能由一个线程打印26个字母)

 死锁:

死锁原因:

避免死锁 

读写锁 

创建一把读写锁 

初始化一把读写锁 

静态初始化,普通赋值

动态初始化,使用函数pthread_rwlock_init() 

销毁一把读写锁pthread_rwlock_destroy() 

加锁 

读模式下加锁pthread_rwlock_rdlock()

读模式下尝试加锁pthread_rwlock_tryrdlock()

带倒计时的读模式下加锁pthread_rwlock_timedrdlock()

写模式下加锁pthread_rwlock_wrlock()

写模式下尝试加锁pthread_rwlock_trywrlock()

带倒计时的写模式下加锁pthread_rwlock_timedwrlock()

解锁pthread_rwlock_unlock()

实例 

条件变量

创建一个条件变量

初始化一个条件变量

静态初始化

动态初始化,使用函数pthread_cond_init()

销毁一个条件变量pthread_cond_destroy()

线程阻塞等待条件变量

无条件等待pthread_cond_wait()

 时间等待pthread_cond_timedwait()

唤醒等待条件变量的线程

随机唤醒某个等待该条件变量的阻塞线程pthread_cond_signal()

 唤醒所有等待该条件变量的阻塞线程 pthread_cond_broadcast()

线程1和线程2并发,线程1阻塞等待,线程2打印26个字母后,由线程2唤醒线程1


线程概念

线程与进程的区别

  • 进程是线程的容器,每个进程中至少有一个线程,一个进程在同一时间至少能做一件事,有了多线程后,在同一时间就能做多件事。
  • 进程是资源分配(CPU时间、内存等)的最小单位,而线程是程序执行的最小单位。
  • 每个进程有自己独立的地址空间,而线程没有自己独立的地址空间,多个线程共享着同一个进程的地址空间。因此在多线程系统中,一个线程崩溃死掉后,整个进程都会死掉,而在多进程系统中,一个进程崩溃后,对其他进程却没有影响,因此多进程的系统比多线程的系统健壮性要高得多。
  • 进程是程序执行的一个实例,线程是操作系统能够进行运算调度的最小单位。

为什么要使用线程

  • 与多线程相比,多进程是一种非常昂贵的多任务操作模式,多进程需要开辟极大的地址空间来存放其代码段、数据段、堆栈段,而在多线程中,线程共享着一个进程的大部分数据,使用着相同的地址空间,开辟的地址空间较小。
  • 线程之间切换所需的时间也远远小于进程间切换所需的时间。
  • 由于每个进程具有自己独立的地址空间,因此在多进程系统中进行数据传递,只能依靠进程间的通信,既费时又不方便。在一个进程中,多个线程共享着一个进程的数据空间,因此线程间对共享数据的访问也更加简单便捷,但也带来了一个问题,有些共享资源要求在同一时刻只能由一个线程进行访问,我们需要利用互斥量来对共享资源上锁

线程标识符

  • 每个线程都有唯一的线程标识符,使用(long unsigned)无符号长整型pthread_t表示线程ID
  •  线程ID的数据类型:pthread_t tid
  • 获取线程ID:pthread_t pthread_self(void)
  • 比较两个线程ID:int pthread_equal(pthread_t t1, pthread_t t2)

 实例

#include <stdio.h>
#include <pthread.h>

int main()
{
    pthread_t tid;
    tid = pthread_self();
    printf("ID of the current pthread is %ld\n",tid);
    return 0;
}

 

线程相关函数 

线程创建函数pthread_create()

原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg); 

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_t *thread:pthread_t变量地址,用来保存创建的线程ID
  • const pthread_attr_t *attr:线程属性,一般传NULL(线程默认属性)
  • void *(*start_routine) (void *):指针函数,创建的线程执行的函数,参数只有一个无类型指针,如果需要多个参数,则使用void *arg传入
  • void *arg:指针函数需要的参数,可以用结构体封装

线程等待函数pthread_join()

原型:int pthread_join(pthread_t thread, void **retval);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_t thread:线程ID
  • void **retval:无类型的二级指针,存储线程退出函数的返回值,如果对返回值不感兴趣,设置为NULL
  • 二级指针:

线程退出函数pthread_exit()

原型:void pthread_exit(void *retval);

参数:

  • void *retval:一级指针,线程退出时传出的数据,不关心其传出数据设置为NULL,传出数据能够被其他线程用pthread_join()函数捕获到

实例(不考虑线程退出状态的线程)

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

struct Msg
{
    char *name;
    int age;
    float score;
};

void *pthread_fun1(void *arg)
{
	//由于线程共用进程的地址空间,对于线程需要的参数,我们可以在进程中设立成全局变量
    char *name = ((struct Msg*)arg)->name;
    int age = ((struct Msg*)arg)->age;
    int tmp = 0;
    while(1){
        printf("thread1 name is %s\n",name);
        printf("thread1 age is %d\n",age);
        sleep(1);
        tmp++;
        if(tmp == 5){
            break;
        }
    }
    pthread_exit(NULL);//不考虑线程1退出状态
}

void *pthread_fun2(void *arg)
{
    char *name = ((struct Msg*)arg)->name;
    int age = ((struct Msg*)arg)->age;
    int tmp = 0;
    while(1){
        printf("thread2 name is %s\n",name);
        printf("thread2 age is %d\n",age);
        sleep(1);
        tmp++;
        if(tmp == 5){
            break;
        }
    }
    pthread_exit(NULL);//不考虑线程1退出状态

}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;
    
	struct Msg m1 = {
        .name = "jiangxiaoya",
        .age = 19,
    };
    struct Msg m2 = {
        .name = "haozige",
        .age = 21,
    };

    pthread_create(&fun1_tid,NULL,pthread_fun1,&m1);
    pthread_create(&fun2_tid,NULL,pthread_fun2,&m2);

    pthread_join(fun1_tid,NULL);//主线程阻塞等待线程1退出
    printf("thread_fun1 is done!\n");
    pthread_join(fun2_tid,NULL);//主线程阻塞等待线程1退出
    printf("thread_fun2 is done!\n");

    printf("over\n");
    return 0;
}

实例(考虑线程退出状态的线程) 

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

struct Msg
{
    char *name;
    int age;
    float score;
};

void *pthread_fun1(void *arg)
{
    static int status = 66;
    char *name = ((struct Msg*)arg)->name;
    int age = ((struct Msg*)arg)->age;
    int tmp = 0;
    while(1){
        printf("thread1 name is %s\n",name);
        printf("thread1 age is %d\n",age);
        sleep(1);
        tmp++;
        if(tmp == 5){
            break;
        }
    }
    pthread_exit((void *)&status);
}

void *pthread_fun2(void *arg)
{
    static char status = 'z';
    char *name = ((struct Msg*)arg)->name;
    int age = ((struct Msg*)arg)->age;
    int tmp = 0;
    while(1){
        printf("thread2 name is %s\n",name);
        printf("thread2 age is %d\n",age);
        sleep(1);
        tmp++;
        if(tmp == 5){
            break;
        }
    }
    pthread_exit((void *)&status);

}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;
    struct Msg m1 = {
        .name = "jiangxiaoya",
        .age = 19,
    };
    struct Msg m2 = {
        .name = "haozige",
        .age = 21,
    };

    pthread_create(&fun1_tid,NULL,pthread_fun1,&m1);
    pthread_create(&fun2_tid,NULL,pthread_fun2,&m2);

    int *tmp1 = NULL;
    char *tmp2 = NULL;


    pthread_join(fun1_tid,(void **)&tmp1);
    printf("thread_fun1 is done!\n");
    printf("thread1 exit status is %d\n",*tmp1);
    pthread_join(fun2_tid,(void **)&tmp2);
    printf("thread_fun2 is done!\n");
    printf("thread2 exit status is %c\n",*tmp2);

    printf("over\n");
    return 0;
}

线程取消函数pthread_cancel()

同一进程中,某个线程调用此函数取消特定线程ID的线程(发送退出请求),被发送请求的线程是否接受该请求取决于它自身

原型:int pthread_cancel(pthread_t thread);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_t thread:线程ID

登记线程退出函数pthread_cleanup_push() 

被登记的函数出现下列其中之一条件就会执行,其中登记顺序与执行顺序相反

  • 线程调用pthread_exit()
  • 线程响应同一个进程的其他线程的取消请求时
  • 用非0参数调用pthread_cleanup_pop(),当只有该条件被满足时,但参数值是0,那么登记函数也不会执行

原型:void pthread_cleanup_push(void (*rtn)(void *),void *arg) ;

参数:

  • void (*rtn)(void *):登记函数的函数名
  • void *arg:传入登记函数的参数

与pthread_cleanup_pop()成对出现函数phread_cleanup_pop()

使用了几个 pthread_cleanup_push() ,就要执行几个pthread_cleanup_pop(),否则会导致编译错误

原型:void pthread_cleanup_pop(int execule);

参数:

  • int execule:0或其他整型值

实例 (线程2的循环体执行五次后请求线程1退出,线程1退出后执行登记函数)

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

pthread_t tmp_tid;

//登记函数1
void myPthreadExit1(void *arg)
{
    printf("myPthreadExit1: %s\n",(char *)arg);
}

//登记函数2
void myPthreadExit2(void *arg)
{
    printf("myPthreadExit2: %s\n",(char *)arg);
}

void *pthread_fun1(void *arg)
{
    pthread_cleanup_push(myPthreadExit1,"argExit1");  //登记线程退出后执行的函数1
    pthread_cleanup_push(myPthreadExit2,"argExit2");  //登记线程退出后执行的函数2

    while(1){
        printf("pthread_fun1\n");
        sleep(1);

    }

	//必须与pthread_cleanup_push()函数成对出现,因为要是线程退出时不满足其他条件时,必须执行这一个条件
    pthread_cleanup_pop(1);  //由于线程退出时满足了条件:线程响应同一个进程的其他线程的取消请求
    pthread_cleanup_pop(1);  //因此不关心该参数的值

    pthread_exit(NULL);
}

void *pthread_fun2(void *arg)
{
    int cnt = 0;
    while(1){
        cnt++;
        printf("pthread_fun2,cnt: %d\n",cnt);
        sleep(1);
        if(cnt == 5){
            pthread_cancel(tmp_tid);  //向线程1发出线程退出请求
        }
        if(cnt == 10){
            break;
        }
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;

    pthread_create(&fun1_tid,NULL,pthread_fun1,NULL);
    pthread_create(&fun2_tid,NULL,pthread_fun2,NULL);

    tmp_tid = fun1_tid;

    pthread_join(fun1_tid,NULL);
    pthread_join(fun2_tid,NULL);

    return 0;
}

互斥量

  • 由于线程共用同一个进程的地址空间,因此有时候我们需要对共享资源上锁,使其在一个时间段只能由一个线程访问,互斥量就可以充当这一把锁
  • 互斥量能对共享资源的进行锁定,能保证在同一时刻只有一个线程去操作该共享资源,使得共享资源变成临界资源,类似于信号量。

当不对共享资源上锁时,多个线程都能访问这个共享资源,导致单个线程无法完整输出26个字母

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *str = "abcdefghijklmnopqrstuvwxyz";

void *pthread_fun1(void *arg)
{
    while(*str != '\0'){
        sleep(1);
        printf("pthread1:%c\n",*str);
        *str++;
    }
    pthread_exit(NULL);
}

void *pthread_fun2(void *arg)
{
    while(*str != '\0'){
        sleep(1);
        printf("pthread2:%c\n",*str);
        *str++;
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;

    pthread_create(&fun1_tid,NULL,pthread_fun1,NULL);
    pthread_create(&fun2_tid,NULL,pthread_fun2,NULL);

    pthread_join(fun1_tid,NULL);
    printf("thread_fun1 is done!\n");
    pthread_join(fun2_tid,NULL);
    printf("thread_fun2 is done!\n");

    printf("over\n");
    return 0;
}

 

创建一把锁

phtread_mutex_t my_mutex; 

该定义成一个全局变量,让该进程内的所有线程都能访问

初始化一把锁

静态初始化,普通赋值

给创建的锁赋值为PTHREAD_MUTEX_INITIALIZER。

动态初始化,使用函数pthread_mutex_init()

原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

返回值:调用成功返回0,调用失败返回错误编号

参数:

pthread_mutex_t *restrict mutex:锁地址

const pthread_mutexattr_t *restrict attr:互斥量属性,一般传NULL,线程共享

销毁一把锁pthread_mutex_destroy()

原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_mutex_t *mutex:锁地址

加锁

加锁pthread_mutex_lock()

原型:int pthread_mutex_lock(pthread_mutex_t *mutex);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_mutex_t *mutex:锁地址

尝试加锁pthread_mutex_trylock()

原型:int pthread_mutex_trylock(pthread_mutex_t *mutex);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_mutex_t *mutex:锁地址

带倒计时的加锁pthread_mutex_timedlock()

在倒计时结束前,与普通加锁没什么区别,倒计时结束后,不再阻塞等待,而是返回一个错误编号

原型:int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_mutex_t *restrict mutex:初始化的锁地址
  • const struct timespec:设定倒计时的时间结构体

解锁pthread_mutex_unlock()

原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_mutex_t *mutex:锁地址

实例(使用互斥量锁住共享资源,让一个时间段只能由一个线程打印26个字母)

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *str = "abcdefghijklmnopqrstuvwxyz";

pthread_mutex_t my_mutex;

void *pthread_fun1(void *arg)
{
    pthread_mutex_lock(&my_mutex);
    while(*str != '\0'){
        sleep(1);
        printf("pthread1:%c\n",*str);
        *str++;
    }
    str = "abcdefghijklmnopqrstuvwxyz";

    pthread_mutex_unlock(&my_mutex);
    pthread_exit(NULL);
}

void *pthread_fun2(void *arg)
{
    pthread_mutex_lock(&my_mutex);
    while(*str != '\0'){
        sleep(1);
        printf("pthread2:%c\n",*str);
        *str++;
    }
    str = "abcdefghijklmnopqrstuvwxyz";

    pthread_mutex_unlock(&my_mutex);
    pthread_exit(NULL);
}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;

    pthread_mutex_init(&my_mutex,PTHREAD_MUTEX_TIMED_NP);

    pthread_create(&fun1_tid,NULL,pthread_fun1,NULL);
    pthread_create(&fun2_tid,NULL,pthread_fun2,NULL);

    pthread_join(fun1_tid,NULL);
    printf("thread_fun1 is done!\n");
    pthread_join(fun2_tid,NULL);
    printf("thread_fun2 is done!\n");

    pthread_mutex_destroy(&my_mutex);

    printf("over\n");
    return 0;
}

一把互斥锁能有多个想要加锁的线程同时去抢,抢到的线程先执行锁住的代码段,没抢到的线程则阻塞等待,直到抢到该互斥锁的线程重新解锁,线程们才会重新抢锁。 

注意:

对于一个全局变量,A线程与B线程竞争加锁访问它时,A线程抢先上锁,B线程只能阻塞等待,但C线程不加锁直接访问该全局变量,仍然能访问到该全局变量,但有可能造成数据混乱。

 死锁:

死锁原因:

  • 当一个线程试图对一个互斥量进行两次加锁,那么将会陷入死锁状态。
  • 当进程有一个以上的互斥量时(例如互斥量a,互斥量b),线程1按照互斥量a、互斥量b的顺序获得锁,线程2按照互斥量b、互斥量a的顺序获得锁就会陷入死锁状态。

    因为当线程1已经使用互斥量a进行加锁,线程2已经使用互斥量b进行加锁,此时线程1想使用互斥量b进行加锁,而线程2想使用互斥量a进行加锁,此时两个线程都加不到想要加的锁,因此无法向前运行,就会陷入死锁状态

避免死锁 

  • 在初始化锁时,把锁初始化成检错锁即传入属性PTHREAD_MUTEX_ERRORCHECK_NP 
  • 使用带倒计时的加锁pthread_mutex_timedlock()

读写锁 

读写锁类似互斥量,也是对共享资源进行锁定,互斥量只有锁住状态和不加锁状态,而读写锁有三种状态

  • 读模式加锁状态:相当于共享锁,线程以读加锁访问时允许访问,以写加锁访问时就会被阻塞
  • 写模式加锁状态:相当于互斥量,线程无论以读加锁访问还是写加锁访问都会被阻塞
  • 写模式不加锁状态

对于存放共享资源并用读写锁加锁的房子,第一个加锁的线程决定了这把读写锁的状态,若该读写锁处于读加锁状态,则其他线程能以读加锁的方式进入该房子,若该锁处于写加锁的状态,则其他线程无论以读加锁的方式还是写加锁的方式都无法进入该房子。 

创建一把读写锁 

pthread_rwlock_t myLock; 

该定义成一个全局变量,让该进程内的所有线程都能访问

初始化一把读写锁 

静态初始化,普通赋值

创建的读写锁赋值为PTHREAD_RWLOCK_INITIALIZER

myLock = PTHREAD_RWLOCK_INITIALIZER;

动态初始化,使用函数pthread_rwlock_init() 

原型:int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *restrict rwlock:读写锁的地址
  • const pthread_rwlockattr_t :读写锁的属性,默认为NULL

销毁一把读写锁pthread_rwlock_destroy() 

原型:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *restrict rwlock:读写锁的地址

加锁 

读模式下加锁pthread_rwlock_rdlock()

原型:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *rwlock:读写锁地址

读模式下尝试加锁pthread_rwlock_tryrdlock()

原型:int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *rwlock:读写锁地址

带倒计时的读模式下加锁pthread_rwlock_timedrdlock()

原型:int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,const struct timespec *restrict abstime);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *restrict rwlock:初始化后的读写锁地址
  • const struct timespec *restrict abstime:设定倒计时的时间结构体

写模式下加锁pthread_rwlock_wrlock()

原型:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *rwlock:读写锁地址

写模式下尝试加锁pthread_rwlock_trywrlock()

原型:int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *rwlock:读写锁地址

带倒计时的写模式下加锁pthread_rwlock_timedwrlock()

原型:int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,const struct timespec *restrict abstime);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *restrict rwlock:初始化后的读写锁地址
  • const struct timespec *restrict abstime:设定倒计时的时间结构体

解锁pthread_rwlock_unlock()

原型:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_rwlock_t *rwlock:读写锁地址

实例 

第一个线程如果是以读模式加锁,那么其他线程读加锁的代码块也能执行,写加锁的代码块则阻塞等待,第一个线程如果以写模式加锁,那么其他线程无论是读加锁的代码块还是写加锁的代码块都会阻塞等待。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

pthread_rwlock_t myLock;

char str[32] = "abcdefghijklmnopqrstuvwxyz";

void *pthread_fun1(void *arg)
{
    //sleep(1);
    pthread_rwlock_wrlock(&myLock);
    for(int i=0;i<26;i++){
        str[i] = 'a';
        printf("thread1: %c\n",str[i]);
        sleep(1);
    }
    pthread_rwlock_unlock(&myLock);
}

void *pthread_fun2(void *arg)
{
    //sleep(1);
    pthread_rwlock_wrlock(&myLock);
    for(int i=0;i<26;i++){
        str[i] = 'b';
        printf("thread2: %c\n",str[i]);
        sleep(1);
    }
    pthread_rwlock_unlock(&myLock);
}

void *pthread_fun3(void *arg)
{
    pthread_rwlock_rdlock(&myLock);
    for(int i=0;i<26;i++){
        str[i] = 'c';
        printf("thread3: %c\n",str[i]);
        sleep(1);
    }
    pthread_rwlock_unlock(&myLock);
}


void *pthread_fun4(void *arg)
{
    pthread_rwlock_rdlock(&myLock);
    for(int i=0;i<26;i++){
        str[i] = 'd';
        printf("thread4: %c\n",str[i]);
        sleep(1);
    }
    pthread_rwlock_unlock(&myLock);
}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;
    pthread_t fun3_tid;
    pthread_t fun4_tid;

    pthread_rwlock_init(&myLock,NULL);

    pthread_create(&fun1_tid,NULL,pthread_fun1,NULL);
    pthread_create(&fun2_tid,NULL,pthread_fun2,NULL);
    pthread_create(&fun3_tid,NULL,pthread_fun3,NULL);
    pthread_create(&fun4_tid,NULL,pthread_fun4,NULL);

    pthread_join(fun1_tid,NULL);
    printf("thread_fun1 is done!\n");
    pthread_join(fun2_tid,NULL);
    printf("thread_fun2 is done!\n");
    pthread_join(fun3_tid,NULL);
    printf("thread_fun3 is done!\n");
    pthread_join(fun4_tid,NULL);
    printf("thread_fun4 is done!\n");

    pthread_rwlock_destroy(&myLock);

    printf("over\n");
    return 0;
}

条件变量

让线程之间产生联系,一个线程阻塞等待另一个线程给他发送信号后继续运行,需要搭配互斥量使用

创建一个条件变量

pthread_cond_t my_cond;

该定义成一个全局变量,让该进程内的所有线程都能访问

初始化一个条件变量

静态初始化

给创建的条件变量赋值为PTHREAD_COND_INITIALIZER。

动态初始化,使用函数pthread_cond_init()

原型:int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_cond_t *restrict cond:条件变量地址
  • const pthread_condattr_t *restrict attr:一般传NULL,条件变量的默认属性

销毁一个条件变量pthread_cond_destroy()

原型:int pthread_cond_destroy(pthread_cond_t *cond);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_cond_t *restrict cond:条件变量地址

线程阻塞等待条件变量

无条件等待pthread_cond_wait()

如果没有被唤醒则会一直阻塞等待下去

原型:int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_cond_t *restrict cond:条件变量地址
  • pthread_mutex_t *restrict mutex:锁地址

 时间等待pthread_cond_timedwait()

线程阻塞等待一段时间,超过这段时间后仍然没有被唤醒则会自己唤醒自己,不再阻塞等待

原型:int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_cond_t *restrict cond:条件变量地址
  • pthread_mutex_t *restrict mutex:锁地址
  • const struct timespec *restrict abstime:线程阻塞等待时间,超过该时间不再阻塞等待

唤醒等待条件变量的线程

随机唤醒某个等待该条件变量的阻塞线程pthread_cond_signal()

原型:int pthread_cond_signal(pthread_cond_t *cond);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_cond_t *restrict cond:条件变量地址

 唤醒所有等待该条件变量的阻塞线程 pthread_cond_broadcast()

原型:int pthread_cond_broadcast(pthread_cond_t *cond);

返回值:调用成功返回0,调用失败返回错误编号

参数:

  • pthread_cond_t *restrict cond:条件变量地址

线程1和线程2并发,线程1阻塞等待,线程2打印26个字母后,由线程2唤醒线程1

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *str = "abcdefghijklmnopqrstuvwxyz";

pthread_mutex_t my_mutex;
pthread_cond_t my_cond;

void *pthread_fun1(void *arg)
{
    printf("thread1 is wait thread2\n");
    pthread_cond_wait(&my_cond,&my_mutex);
    for(int i=0;i<5;i++){
        printf("haozige\n");
        sleep(1);
    }
    pthread_exit(NULL);
}

void *pthread_fun2(void *arg)
{
    while(*str != '\0'){
        sleep(1);
        printf("pthread2:%c\n",*str);
        *str++;
    }
    pthread_cond_signal(&my_cond);
    pthread_exit(NULL);
}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;

    pthread_mutex_init(&my_mutex,PTHREAD_MUTEX_TIMED_NP);
    pthread_cond_init(&my_cond,NULL);

    pthread_create(&fun1_tid,NULL,pthread_fun1,NULL);
    pthread_create(&fun2_tid,NULL,pthread_fun2,NULL);

    pthread_join(fun1_tid,NULL);
    printf("thread_fun1 is done!\n");
    pthread_join(fun2_tid,NULL);
    printf("thread_fun2 is done!\n");

    pthread_mutex_destroy(&my_mutex);
    pthread_cond_destroy(&my_cond);

    printf("over\n");
    return 0;
}

 线程1和线程2并发,线程1阻塞等待5s钟,线程2打印26个字母后,由线程2唤醒线程1

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

char *str = "abcdefghijklmnopqrstuvwxyz";

pthread_mutex_t my_mutex;
pthread_cond_t my_cond;

void *pthread_fun1(void *arg)
{
    struct timespec time;
    clock_gettime(CLOCK_REALTIME,&time); //获取自1970-01-01 00:00:00到此刻的秒数和纳秒数
    time.tv_sec += 5; //阻塞5s
    time.tv_nsec = 0; //纳秒清0
    printf("thread1 is wait thread2\n");
    pthread_cond_timedwait(&my_cond,&my_mutex,&time);
    for(int i=0;i<5;i++){
        printf("haozige\n");
        sleep(1);
    }
    pthread_exit(NULL);
}


void *pthread_fun2(void *arg)
{
    while(*str != '\0'){
        sleep(1);
        printf("pthread2:%c\n",*str);
        *str++;
    }
    pthread_cond_signal(&my_cond);
    pthread_exit(NULL);
}

int main()
{
    pthread_t fun1_tid;
    pthread_t fun2_tid;

    pthread_mutex_init(&my_mutex,PTHREAD_MUTEX_TIMED_NP);
    pthread_cond_init(&my_cond,NULL);

    pthread_create(&fun1_tid,NULL,pthread_fun1,NULL);
    pthread_create(&fun2_tid,NULL,pthread_fun2,NULL);

    pthread_join(fun1_tid,NULL);
    printf("thread_fun1 is done!\n");
    pthread_join(fun2_tid,NULL);
    printf("thread_fun2 is done!\n");

    pthread_mutex_destroy(&my_mutex);
    pthread_cond_destroy(&my_cond);

    printf("over\n");
    return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中的多线程实际上是通过进程来模拟实现的。在Linux中,多个线程是通过共享父进程的资源来实现的,而不是像其他操作系统那样拥有自己独立的线程管理模块。因此,在Linux中所谓的“线程”其实是通过克隆父进程的资源而形成的“线程”。这也是为什么在Linux中所说的“线程”概念需要加上引号的原因。 对于Linux中的线程,需要使用线程库来进行管理。具体来说,Linux中的线程ID(pthread_t类型)实质上是进程地址空间上的一个地址。因此,要管理这些线程,需要在线程库中进行描述和组织。 由于Linux中没有真正意义上的线程,因此线程的管理和调度都是由线程库来完成的。线程库负责创建线程、终止线程、调度线程、切换线程,以及为线程分配资源、释放资源和回收资源等任务。需要注意的是,线程的具体实现取决于Linux的实现,目前Linux使用的是NPTL(Native POSIX Thread Library)。 总结来说,Linux中的多线程是通过进程来模拟实现的,线程共享父进程的资源。线程的管理和调度由线程库完成。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Linux —— 多线程](https://blog.csdn.net/sjsjnsjnn/article/details/126062127)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值