(自用)多线程编程

1.为什么要使用线程

  1. 使用fork创建进程以执行新的任务,该方式的代价很高。(子进程将父进程所有资源复制一遍)
  2. 多个进程间不会直接共享内存
  3. 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行,进程要想执行任务,必须得有线程,进程至少要有一条线程,程序启动会默认开启一条线程,这条线程被称为主线程或 UI 线程

2. 什么是线程

线程,是进程内部的一个控制序列。

即使不使用线程,进程内部也有一个执行线程。

    类比:创建一个进程,类似于“克隆”一个家庭。

            该“家庭”与原来的家庭完全相同

            但是新“家庭”和原来的家庭完全独立。

进程包含一个或多个线程。

            类似与一个家庭,包含一个或多个家庭成员。

             家庭内的各成员同时做各自的事情(父亲工作、母亲持家、小孩上学)

             而对于家庭外部的人来说,这个家庭同时在做多件事情。

             

             家庭内的每个成员,就是一个线程。

             各个家庭成员有自己的个人资源(线程有自己的局部变量)

             但是所有家庭成员都能共享这个家庭的资源:房子、汽车、家庭的公共资金。

             (同一个进程内的各个线程,能够共享整个进程的全局变量,除了线程的局部变量外,其他资源都共享)

             

    注意:单核处理器上,同一个时刻只能运行一个线程。

             但是对于用户而言,感觉如同同时执行了多个线程一样

            (各线程在单核CPU上切换,在一段时间内,同时执行了多个线程)

3. 线程的优点、缺点

    优点: 创建线程比创建进程,开销要小。

    缺点: 1)多线程编程,需特别小心,很容易发生错误。

           2)多线程调试很困难。

           3)把一个任务划分为两部分,

                   用两个线程在单处理器上运行时,不一定更快。

                   除非能确定这两个部分能同时执行、且运行在多处理器上。

4. 线程的应用场合

    1) 需要让用户感觉在同时做多件事情时,

        比如,处理文档的进程,一个线程处理用户编辑,一个线程同时统计用户的字数。

    

    2) 当一个应用程序,需要同时处理输入、计算、输出时,

        可开3个线程,分别处理输入、计算、输出。

        让用户感觉不到等待。

   3) 高并发编程。

5. 线程的使用

相关头文件

#include <pthread.h>

1)线程的创建

       pthread_create

       原型:int  pthread_create (

                         pthread_t *thread,

                         pthread_attr_t *attr,

                         void *(*start_routine)(void*),

                         void *arg);                                              

                         

       参数:thread, 指向新线程的标识符。

                           通过该指针返回所创建线程的标识符。

               attr, 用来设置新线程的属性。

                      一般取默认属性,即该参数取NULL

               start_routine, 该线程的处理函数

                                    该函数的返回类型和参数类型都是void*

               arg, 线程处理函数start_routine的参数

               

       功能:创建一个新线程,

               同时指定该线程的属性、执行函数、执行函数的参数

               通过参数1返回该线程的标识符。

       

       返回值:成功,返回0

                  失败,返回错误代码                  

                  注意:大部分pthread_开头的函数成功时返回0,失败时返回错误码(而不是-1)

                  

       注意:使用fork创建进程后,进程马上就启动,但是是和父进程同时执行fork后

的代码。

             使用pthread_create创建线程后,新线程马上就启动,即执行对应的线程处理函数。

2)线程的终止

pthread_exit

       原型:void  pthread_exit (void *retval)

       功能:在线程函数内部调用该函数。

                终止该线程,并通过参数retval返回一个指针。

                该指针不能指向该线程的局部变量。   

3)等待指定线程结束

       pthread_join

       功能:类似与进程中的waitpid

                等待指定的线程结束,并使参数指向该线程函数的返回值(用pthread_exit返回的值)

       原型:int  pthread_join  (pthread_t   th,

                                 void ** thread_return);

       参数:th,  指定等待的线程

               thread_return, 指向该线程函数的返回值

                                      线程函数的返回值类型为void*,故该参数的类型为void**

4)使用线程程序的编译

         (1) 编译时,定义宏_REENTRANT

               即: gcc -D_REENTRANT  (#define REENTRANT)

               

               功能:告诉编译器,编译时需要可重入功能。

                       即使得,在编译时,编译部分函数的可重入版本。

:在单线程程序中,整个程序都是顺序执行的,一个函数在同一时刻只能被一个函数调用,但在多线程中,由于并发性,一个函数可能同时被多个函数调用,此时这个函数就成了临界资源,很容易造成调用函数处理结果的相互影响,如果一个函数在多线程并发的环境中每次被调用产生的结果是不确定的,我们就说这个函数是"不可重入的"/"线程不安全"的。

                       

         (2) 编译时,指定线程库

              即: gcc -lpthread

              功能:使用系统默认的NPTL线程库,

                       即在默认路径中寻找库文件libpthread.so

                       默认路径为/usr/lib和/usr/local/lib

              

              当系统默认使用的不是NPTL线程库时(系统较老,2003年以前)

              指定:gcc  -L/usr/lib/nptl   -lpthread

              补充: -L 指定库文件所在的目录

                       -l  指定库文件的名称(-lpthread ,指库文件名为libpthread.so)

              

          总结:一般使用如下形式即可

                 gcc   -D_REENTRANT   -lpthread    mythread.c    -o   mythread  

代码示例

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

int my_global;

void* my_thread_handle(void* arg)//线程要执行的任务
{
	int val;

	val = *((int*)arg);//获取传进来的参数

	printf("new thread begin, arg=%d\n", val);
	my_global += val;

	sleep(3);

	pthread_exit(&my_global);

	//  不再执行
	printf("new thread end\n");
}

int main(void)
{
	pthread_t  mythread;//创建线程标识符
	int arg;//作为线程任务函数的传参
	int ret;
	void* thread_return;//获取线程退出的状态值

	arg = 100;
	my_global = 1000;

	printf("my_global=%d\n", my_global);
	printf("ready create thread...\n");
	//第二个参数为0表示默认属性
	ret = pthread_create(&mythread, 0, my_thread_handle, &arg);
	if (ret != 0) {
		printf("create thread failed!\n");
		exit(1);
	}

	printf("wait thread finished...\n");
	ret = pthread_join(mythread, &thread_return);//第二个参数为void**类型
	if (ret != 0) {
		printf("pthread_join failed!\n");
		exit(1);
	}
	printf("wait thread end, return value is %d\n", *((int*)thread_return));
	printf("my_global=%d\n", my_global);

	printf("create thread finished!\n");
}


5)线程的同步

1)线程的互斥 - 指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

2)线程的同步 - 指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。

1. 问题

    同一个进程内的各个线程,共享该进程内的全局变量

    如果多个线程同时对某个全局变量进行访问时,就可能导致竞态。

    解决办法: 对临界区使用信号量、或互斥量。

    

2. 信号量和互斥量的选择。

    对于同步和互斥,使用信号量或互斥量都可以实现。

    使用时,选择更符合语义的手段:

               如果要求最多只允许一个线程进入临界区,则使用互斥量

               如果要求多个线程之间的执行顺序满足某个约束,则使用信号量

     

 信号量

相关头文件

#include <semaphore.h>

    1)什么是信号量

         此时所指的“信号量”是指用于同一个进程内多个线程之间的信号量。

         即POSIX信号量,而不是System V信号量(用于进程之间的同步)

POSIX信号量——线程间实现同步

System V信号量——进程间实现同步         

         用于线程的信号量的原理,与用于进程之间的信号量的原理相同。

         都有P操作、V操作。

         

         信号量的表示:sem_t  类型

         

    2)  信号量的初始化

         sem_init    

         原型:int  sem_init  (sem_t  *sem,   int  pshared,   unsigned int value);

         功能:对信号量进行初始化

         参数:sem,  指向被初始化的信号量

               pshared,  0:表示该信号量是该进程内使用的“局部信号量”, 不再被其它进程共享。非0:该信号量可被其他进程共享,Linux不支持这种信号量

                 value,  信号量的初值。>= 0

         返回值:成功,返回0

                    失败, 返回错误码

                    

    3) 信号量的P操作

         sem_wait

         原型:int   sem_wait (sem_t  *sem);

         返回值:成功,返回0

                 失败, 返回错误码

    

    4) 信号量的V操作

         sem_post

         原型:int  sem_post (sem_t  *sem);

         返回值:成功,返回0

                    失败, 返回错误码

    

    5) 信号量的删除

        sem_destroy

        原型:int  sem_destroy (sem_t  *sem);

        返回值:成功,返回0

                   失败, 返回错误码

   

    6) 实例

        主线程循环输入字符串,把字符串存放到一个全局缓存中。

        新线程从全局缓存中读取字符串,统计该字符串的长度。

        直到用户输入end

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

#define BUFF_SIZE 80

char buff[BUFF_SIZE];
sem_t sem;

static void* str_thread_handle(void *arg) 
{
	while(1) {
		//P(sem)
		if (sem_wait(&sem) != 0) {
			printf("sem_wait failed!\n");
			exit(1);
		}
		
		printf("string is: %slen=%d\n", buff, strlen(buff));
		if (strncmp(buff, "end", 3) == 0) {
			break;
		}
	}
}

int main(void)
{
	int ret;
	pthread_t  str_thread;
	void *thread_return;


	ret = sem_init(&sem, 0, 0);
	if (ret != 0) {
		printf("sem_init failed!\n");
		exit(1);
	}

	ret = pthread_create(&str_thread, 0, str_thread_handle, 0);
	if (ret != 0) {
		printf("pthread_create failed!\n");
		exit(1);
	}

	while (1) {
		fgets(buff, sizeof(buff), stdin);

		//V(sem)
		if (sem_post(&sem) != 0) {
			printf("sem_post failed!\n");
			exit(1);
		}
		
		if (strncmp(buff, "end", 3) == 0) {
			break;
		}
	}

	ret = pthread_join(str_thread, &thread_return);
	if (ret != 0) {
		printf("pthread_join failed!\n");
		exit(1);
	}

	ret = sem_destroy(&sem);
	if (ret != 0) {
		printf("sem_destroy failed!\n");
		exit(1);
	}

	return 0;
}

 7) 练习

        创建2个线程(共有主线程、线程1、线程2共3个线程)

        主线程阻塞式等待用户输入字符串

        主线程每接收到一个字符串之后, 线程1就马上对该字符串进行处理。

        线程1的处理逻辑为:统计该字符串的个数,并记录当时的时间。

        线程1把该字符串处理完后,线程2马上就把处理结果写入文件result.txt

        直到用户输入exit.

        multi_pthread.c

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<time.h>
#include<fcntl.h>
#include<unistd.h>

#define BUFF_SIZE 80
sem_t sem1;//主 1
sem_t sem2;//1 2
sem_t sem3;//2 主
char buff[BUFF_SIZE];
char message[1024];
int thread_2_end = 0;

void* thread_1_handle(void* arg)
{
	int ret; 
	int len;
	while (1)
	{
		//P操作
		ret = sem_wait(&sem1);
		if (ret != 0)
		{
			fprintf(stderr,"thread 1 P failed.error desc:%s\n",strerror(ret));
			exit(4);
		}
		
		len = strlen(buff);
		time_t t = time(NULL);
		struct tm* local = localtime(&t);
		printf("thread 1 handle str : %s\n strcmp(buff, \"exit\") = %d\n ",buff, strcmp(buff, "exit"));

		sprintf(message,"time:%d-%02d-%02d %02d:%02d:%02d  strlen:%d\n",
			local->tm_year + 1900,
			local->tm_mon + 1,
			local->tm_mday,
			local->tm_hour,
			local->tm_min,
			local->tm_sec,
			len);

		/*if (!strcmp(buff, "exit"))
		{
			thread_2_end = 1;
			printf("thread 1 will exit.\n");
			pthread_exit(NULL);
		}*/
		//V操作
		ret = sem_post(&sem2);
		if (ret != 0)
		{
			fprintf(stderr, "thread 1 V failed.error desc:%s\n", strerror(ret));
			exit(5);
		}

	


	}
}


void* thread_2_handle(void* arg)
{
	int ret;
	int len;
	int fd = open("result.txt",O_CREAT|O_RDWR,0666);
	while (1)
	{
		//P操作
		ret = sem_wait(&sem2);
		/*if (thread_2_end)
		{
			printf("thread 1 will exit.\n");
			pthread_exit(NULL);
		}*/

		if (ret != 0)
		{
			fprintf(stderr, "thread 2 P failed.error desc:%s\n", strerror(ret));
			exit(6);
		}

		len = write(fd,message,strlen(message));
		if (len < 0)
		{
			fprintf(stderr, "write to file failed.error desc:%s\n", strerror(errno));
			exit(6);
		}

		//V操作
		ret = sem_post(&sem3);
		if (ret != 0)
		{
			fprintf(stderr, "thread 2 V failed.error desc:%s\n", strerror(ret));
			exit(6);
		}

		


	}
}



int main()
{
	int ret;
	pthread_t thread1;
	pthread_t thread2;
	ret = sem_init(&sem1, 0, 0);
	if (ret != 0)
	{
		fprintf(stderr,"create semaphore failed.error desc:%s\n",strerror(ret));
		exit(1);
	}
	ret = sem_init(&sem2, 0, 0);
	if (ret != 0)
	{
		fprintf(stderr, "create semaphore failed.error desc:%s\n", strerror(ret));
		exit(2);
	}
	ret = sem_init(&sem3, 0, 0);
	if (ret != 0)
	{
		fprintf(stderr, "create semaphore failed.error desc:%s\n", strerror(ret));
		exit(2);
	}

	ret = pthread_create(&thread1,NULL,thread_1_handle,NULL);
	if (ret != 0)
	{
		fprintf(stderr, "create thread 1 failed.error desc:%s\n", strerror(ret));
		exit(2);
	}

	ret = pthread_create(&thread2, NULL, thread_2_handle, NULL);
	if (ret != 0)
	{
		fprintf(stderr, "create thread 2 failed.error desc:%s\n", strerror(ret));
		exit(2);
	}


	while (1)
	{
		fgets(buff,sizeof(buff),stdin);
		int len = strlen(buff);
		if (buff[len - 1] == '\n') {
			buff[len - 1] = '\0';
		}
		printf("input str : %s",buff);
		
		//V操作
		ret = sem_post(&sem1);
		if (ret != 0)
		{
			fprintf(stderr,"V failed.error desc:%s\n",strerror(ret));
			exit(3);
		}
		if (!strcmp(buff, "exit"))
		{

			return 0;
		}

		//P操作
		ret = sem_wait(&sem3);
		if (ret != 0)
		{
			fprintf(stderr, "main thread P failed.error desc:%s\n", strerror(ret));
			exit(3);
		}
		
	}

	return 0;
}

互斥量

1)什么是互斥量

         效果上等同于初值为1的信号量

         

         互斥量的使用:类型为 pthread_mutex_t

         

    2)互斥量的初始化

         pthread_mutex_init

         原型:int  pthread_mutex_init(pthread_mutex_t *mutex,

                                       pthread_mutexattr_t *attr);

         参数:mutex, 指向被初始化的互斥量

                 attr,  指向互斥量的属性

                         一般取默认属性(当一个线程已获取互斥量后,该线程再次获取该信号量,将导致死锁!)

互斥量属性设置

步骤:

1.定义一个mutex属性变量pthread_mutexattr_t
pthread_mutexattr_t  attr;
2.调用pthread_mutexattr_setxxx()为attr赋值
  1. 互斥锁类型(pshared):pthread_mutexattr_setpshared(&attr)

    • PTHREAD_PROCESS_PRIVATE: 互斥锁为进程私有,只能被同一进程内的线程共享。
    • PTHREAD_PROCESS_SHARED: 互斥锁为进程间共享,可以被不同进程中线程之间共享。
  2. 互斥锁的类型(type):pthread_mutexattr_settype(&attr)

    • PTHREAD_MUTEX_NORMAL: 普通锁,不允许同一线程对互斥锁重复加锁。
    • PTHREAD_MUTEX_ERRORCHECK: 错误检查锁,会进行加锁错误检查。
    • PTHREAD_MUTEX_RECURSIVE: 递归锁,同一线程可以多次对互斥锁加锁。
    • PTHREAD_MUTEX_DEFAULT: 默认锁类型,通常等同于PTHREAD_MUTEX_NORMAL。
  3. 互斥锁的协议(protocol):

    • PTHREAD_PRIO_NONE: 无优先级继承。
    • PTHREAD_PRIO_INHERIT: 优先级继承,互斥锁的拥有者会继承被阻塞线程的优先级,避免优先级反转问题。
    • PTHREAD_PRIO_PROTECT: 优先级保护,即高优先级线程无法获取低优先级线程已经占有的锁。
  4. 互斥锁的饥饿性(robust):

    • PTHREAD_MUTEX_STALLED: 死锁检测。
    • PTHREAD_MUTEX_ROBUST: 死锁恢复,当拥有互斥锁的线程终止后,新的线程能够正常解锁。
3.互斥量带上attr进行定义
 err = pthread_mutex_init(mtx, &attr);
 if (err != 0) {
     fprintf(stderr,"pthread_mutex_init() failed, reason: %s\n",strerror(errno));
     return ERROR;
 }

    

    3) 互斥量的获取

        pthread_mutex_lock

        原型:int  pthread_mutex_lock (pthread_mutex_t *mutex);   

    

    4)互斥量的释放

         pthread_mutex_unlock

         原型:int  pthread_mutex_unlock (pthread_mutex_t  *mutex);         

         

    5)互斥量的删除

         pthread_mutex_destroy

         int  pthread_mutex_destroy (pthread_mutex_t *mutex);  

         

    6) 实例

        最简单的互斥量使用

        main3.c

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

void* thread_function(void* arg);
pthread_mutex_t work_mutex; /* protects both work_area and time_to_exit */

#define WORK_SIZE 1024
char work_area[WORK_SIZE];
int time_to_exit = 0;

int main() {
    int res;
    pthread_t a_thread;
    void* thread_result;
    res = pthread_mutex_init(&work_mutex, NULL);
    if (res != 0) {
        perror("Mutex initialization failed");
        exit(EXIT_FAILURE);
    }
    res = pthread_create(&a_thread, NULL, thread_function, NULL);
    if (res != 0) {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    pthread_mutex_lock(&work_mutex);
    printf("Input some text. Enter 'end' to finish\n");
    while (!time_to_exit) {
        fgets(work_area, WORK_SIZE, stdin);
        pthread_mutex_unlock(&work_mutex);
        while (1) {
            pthread_mutex_lock(&work_mutex);
            if (work_area[0] != '\0') {
                pthread_mutex_unlock(&work_mutex);
                sleep(1);
            }
            else {
                break;
            }
        }
    }
    pthread_mutex_unlock(&work_mutex);
    printf("\nWaiting for thread to finish...\n");
    res = pthread_join(a_thread, &thread_result);
    if (res != 0) {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
    pthread_mutex_destroy(&work_mutex);
    exit(EXIT_SUCCESS);
}

void* thread_function(void* arg) {
    sleep(1);
    pthread_mutex_lock(&work_mutex);
    while (strncmp("end", work_area, 3) != 0) {
        printf("You input %d characters\n", strlen(work_area) - 1);
        work_area[0] = '\0';
        pthread_mutex_unlock(&work_mutex);
        sleep(1);
        pthread_mutex_lock(&work_mutex);
        while (work_area[0] == '\0') {
            pthread_mutex_unlock(&work_mutex);
            sleep(1);
            pthread_mutex_lock(&work_mutex);
        }
    }
    time_to_exit = 1;
    work_area[0] = '\0';
    pthread_mutex_unlock(&work_mutex);
    pthread_exit(0);
}

线程条件变量

 与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

1)条件变量初始化  

    pthread_cond_init

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

参数:cond,  条件变量指针

      attr    条件变量高级属性

man 安装:   apt-get install manpages-posix-dev

2) 唤醒一个等待线程

    pthread_cond_signal 通知条件变量,唤醒一个等待者 

原型: int pthread_cond_signal (pthread_cond_t *cond);

参数:cond,  条件变量指针

      

3)唤醒所有等待该条件变量的线程

pthread_cond_broadcast  广播条件变量

原型: int pthread_cond_broadcast (pthread_cond_t *cond);

参数:cond,  条件变量指针

4)等待条件变量/超时被唤醒

pthread_cond_timedwait  等待条件变量cond被唤醒,直到由一个信号或广播,或绝对时间abstime到 * 才唤醒该线程

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

参数:cond,  条件变量指针

      pthread_mutex_t *mutex 互斥量

      const struct timespec *abstime 等待被唤醒的绝对超时时间

5)等待条件变量被唤醒

pthread_cond_wait  等待条件变量cond被唤醒(由一个信号或者广播)

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

参数:cond,  条件变量指针

      pthread_mutex_t *mutex 互斥量

常见错误码:  [EINVAL] cond或mutex无效,

[EINVAL] 同时等待不同的互斥量  

[EINVAL] 主调线程没有占有互斥量  

6) 释放/销毁条件变量

pthread_cond_destroy  待销毁的条件变量

原型: int pthread_cond_destroy (pthread_cond_t *cond);

参数:cond,  条件变量指针

7)实例

main6.c

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

pthread_mutex_t mutex;
pthread_cond_t cond;

void *thread1(void *arg)
{

	
	while (1) {

		printf("thread1 is running\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		printf("thread1 applied the condition\n");
		pthread_mutex_unlock(&mutex);

		sleep(4);
	}

}


void *thread2(void *arg)
{
	while (1) {

		printf("thread2 is running\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond, &mutex);
		printf("thread2 applied the condition\n");
		pthread_mutex_unlock(&mutex);

		sleep(2);

	}

}

int main()
{

	pthread_t thid1, thid2;

	printf("condition variable study!\n");

	pthread_mutex_init(&mutex, NULL);

	pthread_cond_init(&cond, NULL);

	pthread_create(&thid1, NULL, (void *)thread1, NULL);

	pthread_create(&thid2, NULL, (void *)thread2, NULL);

	do {

		pthread_cond_signal(&cond);
       sleep(1);

	} while (1);

	

	return 0;

}

实例解析

该程序执行时,一有3个线程:

1.主线程重复两个动作:a.唤醒一个等待线程 b.睡眠1s

2.线程1和线程2的工作差不多,一直循环进行:

a.获得锁 b.等待条件变量被唤醒 c.打印 d,睡眠几秒

pthread_mutex_lock(&mutex);//获得锁
pthread_cond_wait(&cond, &mutex);//等待条件变量被唤醒
printf("thread1 applied the condition\n");
pthread_mutex_unlock(&mutex);//解锁

如上图所示,要进行输入要先后满足两个条件:1.获得锁 2.被条件变量唤醒

疑问:pthread_cond_wait(&cond, &mutex)为什么要带上锁mutex?

pthread_cond_wait(&cond, &mutex)可以细分为:

被唤醒前

1.将进程挂起

2.打开mutex锁

被唤醒后

1.给mutex加锁

2.线程继续往下执行

对main6.c进行修改:

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

pthread_mutex_t mutex;
pthread_cond_t cond;

void* thread1(void* arg)
{


	while (1) {

		printf("thread1 is running\n");
		pthread_mutex_lock(&mutex);//获得锁
		printf("thread1 lock.\n");
		pthread_cond_wait(&cond, &mutex);//等待条件变量被唤醒
		printf("thread1 applied the condition\n");
		printf("thread1 unlock.\n");
		pthread_mutex_unlock(&mutex);//解锁

		sleep(4);
	}

}


void* thread2(void* arg)
{
	while (1) {

		printf("thread2 is running\n");
		pthread_mutex_lock(&mutex);
		printf("thread2 lock.\n");
		pthread_cond_wait(&cond, &mutex);
		printf("thread2 applied the condition\n");
		printf("thread2 unlock.\n");
		pthread_mutex_unlock(&mutex);

		sleep(2);

	}

}

int main()
{

	pthread_t thid1, thid2;

	printf("condition variable study!\n");

	pthread_mutex_init(&mutex, NULL);//初始化锁

	pthread_cond_init(&cond, NULL);//初始化条件变量

	pthread_create(&thid1, NULL, (void*)thread1, NULL);

	pthread_create(&thid2, NULL, (void*)thread2, NULL);

	do {
		sleep(10);
		pthread_cond_signal(&cond);//唤醒一个等待者

	} while (1);



	return 0;

}

 输出结果

时间线

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值