线程间通信入门

有名信号量

(1)名字:“/myname”

     注意:名字必须以/开头

(2)使用有名信号量需要用到pthread库(线程库)
复制代码

如何使用这个信号量?

1、通过sem_open(),打开信号量
2、直接通过sem_wait(),sem_post()进行P、V操作
3、通过sem_close()关闭信号量
4、通过sem_unlink()删除信号量,释放资源
复制代码

1、通过sem_open(),打开信号量

image.png

2、直接通过sem_wait(),sem_post()进行P、V操作

image.png

3、通过sem_close()关闭信号量、通过sem_unlink()删除信号量,释放资源

image.png

示例代码

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>

char str[20];

#define SPACE "/space"
#define DATA "/data"

sem_t *space;
sem_t *data; 

void *func1(void *arg)  		
{
	while(1)
	{	
		//P操作				空间资源-1
		sem_wait(space);
		scanf("%s", str);
		//V操作				数据资源+1
		sem_post(data);
	}
}

void *func2(void *arg)			
{
	while(1)
	{
		//P操作				数据资源-1
		sem_wait(data);
		printf("%s\n", str);
		//V操作				空间资源+1
		sem_post(space);
	}
}

void f(int sig)
{
	sem_close(space);
	sem_unlink(SPACE);
	sem_close(data);
	sem_unlink(DATA);
	exit(0);
}

int main(int argc, char const *argv[])
{
	//
	signal(2, f);


	void *p[2] = {func1, func2};
	
	pthread_t pid[2];
	for (int i = 0; i < 2; ++i)
	{
		pthread_create(&pid[i], NULL, p[i], NULL);
	}

	space = sem_open(SPACE, O_CREAT, 0666, 1);
	data = sem_open(DATA, O_CREAT, 0666, 0);

	while(1);


	return 0;
}
复制代码

运行结果

image.png

无名信号量

这种信号量的使用步骤是:

1,在这些线程都能访问到的区域定义这种变量(比如全局变量),类型是 sem_t。
2,在任何线程使用它之前,用 sem_init( )初始化他。
3,使用 sem_wait( )/sem_trywait( )和 sem_post( )来分别进行 P、V 操作。
4,不再需要时,使用 sem_destroy( )来销毁他。
复制代码

image.png

示例代码

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>

char str[20];

sem_t space;
sem_t data; 

void *func1(void *arg)  		
{
	while(1)
	{	
		//P操作				空间资源-1
		sem_wait(&space);
		scanf("%s", str);
		//V操作				数据资源+1
		sem_post(&data);
	}
}

void *func2(void *arg)			
{
	while(1)
	{
		//P操作				数据资源-1
		sem_wait(&data);
		printf("%s\n", str);
		//V操作				空间资源+1
		sem_post(&space);
	}
}

void f(int sig)
{
	sem_destroy(&data);
	sem_destroy(&space);	
	exit(0);
}

int main(int argc, char const *argv[])
{
	//
	signal(2, f);

	sem_init(&space, 0, 1);			//空间资源(写资源)		1
	sem_init(&data, 0, 0);			//数据资源(读资源)		0

	void *p[2] = {func1, func2};
	
	pthread_t pid[2];
	for (int i = 0; i < 2; ++i)
	{
		pthread_create(&pid[i], NULL, p[i], NULL);
	}

	
	while(1);


	return 0;
}
复制代码

image.png

互斥锁:

Pthread_mutex_t m;//定义一个互斥锁变量

//初始化
pthread_mutex_init(&m, NULL);

//上锁
pthread_mutex_lock(&m);

//解锁
pthread_mutex_unlock(&m);

//销毁锁
pthread_mutex_destroy(&m);
复制代码

 

使用互斥锁容易出现死锁

(1)多个线程交替上锁解锁2个锁的时候,有可能出现死锁

Pthread_mutex_lock(&m1);
Printf(“做一件2s的事情”);
Sleep(2);
Pthread_mutex_lock(&m2);
Pthread_mutex_unlock(&m1);
Pthread_mutex_unlock(&m2);


Pthread_mutex_lock(&m2);
Printf(“做一件2s的事情”);
Sleep(2);
Pthread_mutex_lock(&m1);
Pthread_mutex_unlock(&m2);
Pthread_mutex_unlock(&m1);
复制代码

(2)某个线程上锁之后,在没有解锁的情况下,被异常杀死,那么需要获取同一个锁的其他线程会卡死

(3)一个任务上锁过后,未解锁之前,在上同一把锁,也会出现死锁

所以:

      为线程创建取消例程,释放有可能没有释放的

      尽量避免在一组锁的上锁解锁过程中,在次上同一把锁或其他锁

      互斥锁保护的临界区不要太长,也不要嵌套太多函数

示例代码:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>

//定义一个互斥锁变量
pthread_mutex_t m;
pthread_mutex_t m1;

int num;

void *func1(void *arg)
{
	while(1)
	{
		//加锁
		pthread_mutex_lock(&m);
		scanf("%d", &num);
		//解锁
		pthread_mutex_unlock(&m);
		usleep(10);
	}
}

void *func2(void *arg)
{
	sleep(1);
	while(1)
	{
		//加锁
		pthread_mutex_lock(&m);
		printf("num = %d\n", num);
		//解锁
		pthread_mutex_unlock(&m);
		usleep(10);
	}
}

int main(int argc, char const *argv[])
{
	//初始化互斥锁
	pthread_mutex_init(&m, NULL);
	pthread_mutex_init(&m1, NULL);

	//创建两个线程
	pthread_t pid1, pid2;

	pthread_create(&pid1, NULL, func1, NULL);
	pthread_create(&pid2, NULL, func2, NULL);

	while(1)
	{

	}



	return 0;
}
复制代码

 
image.png

读写锁:

读写锁跟互斥锁,几乎使用一模一样,唯一的区别是在加锁的时候,可以选择加写锁还是读锁,写锁,只能加一个,读锁可以加多个

//定义读写锁变量
Pthread_rwlock_t rwlock;

//初始化读写锁
Pthread_rwlock_init(&rwlock, NULL);

 //加读锁
Pthread_rwlock_rdlock(&rwlock);

//加写锁
Pthread_rwlock_wrlock(&rwlock);

//解锁
Pthread_rwlock_unlock(&rwlock);

//销毁锁
Pthread_rwlock_destroy(&rwlock);
复制代码

 示例代码

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>

//定义一个读写锁变量
pthread_rwlock_t rwlock;

int num;

void dent()
{
	pthread_rwlock_rdlock(&rwlock);
	printf("线程1的锁状态\n");
	pthread_rwlock_unlock(&rwlock);
}

void dent1()
{
	pthread_rwlock_wrlock(&rwlock);
	printf("线程2的锁状态\n");
	pthread_rwlock_unlock(&rwlock);
}

void *func1(void *arg)
{
	while(1)
	{
		//加写锁
		pthread_rwlock_rdlock(&rwlock);
		scanf("%d", &num);
		//解锁		
		pthread_rwlock_unlock(&rwlock);
		dent();
		usleep(1000);
	}
}

void *func2(void *arg)
{
	sleep(1);
	while(1)
	{
		//加读锁
		pthread_rwlock_wrlock(&rwlock);
		printf("num2 = %d\n", num);
		//解锁
		pthread_rwlock_unlock(&rwlock);
		dent1();
		usleep(1000);
	}
}


int main(int argc, char const *argv[])
{
	//初始化读写锁
	pthread_rwlock_init(&rwlock, NULL);

	//创建两个线程
	pthread_t pid1, pid2;

	pthread_create(&pid1, NULL, func1, NULL);
	pthread_create(&pid2, NULL, func2, NULL);


	while(1)
	{

	}



	return 0;
}
复制代码

运行结果

image.png

条件变量:

image.png

//定义条件变量的变量

Pthread_cond_t cond;

//初始化

Pthread_cond_init(&cond, 0);

//销毁条件变量

Pthread_cond_destroy(&cond);
复制代码

image.png

//进入条件变量的等待队列
Pthread_cond_wait(&cond, &m);
Struct timespec t;
Pthread_cond_timedwait(&cond, &m,  &t);
复制代码

image.png

//唤醒一个
Pthread_cond_signal(&cond);
//唤醒全部
Pthread_cond_broadcast(&cond);
复制代码

示例代码

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <semaphore.h>

//定义互斥锁变量
pthread_mutex_t m;

//定义条件变量的变量
pthread_cond_t v;

int money = 0;
int flag = 0;

void *func1(void *arg)	//小楠
{
	//加锁
	pthread_mutex_lock(&m);
	while(money < 100)
	{
		pthread_cond_wait(&v, &m);//进入条件变量等待队列
	}

	money -=100;
	printf("小楠拿钱 100\n");
	sleep(1);
	pthread_mutex_unlock(&m);
}

void *func2(void *arg)	//小明
{
	//加锁
	pthread_mutex_lock(&m);
	while(money < 100)
	{
		pthread_cond_wait(&v, &m);//进入条件变量等待队列
	}
	money -=100;
	printf("小明拿钱 100\n");
	sleep(1);
	pthread_mutex_unlock(&m);
}

int main(int argc, char const *argv[])
{
	//初始化互斥锁
	pthread_mutex_init(&m, NULL);
	//初始化条件变量
	pthread_cond_init(&v, 0);

	pthread_t pid1, pid2;
	pthread_create(&pid1, NULL ,func1, NULL);
	pthread_create(&pid2, NULL ,func2, NULL);
	sleep(1);

	
	
	pthread_mutex_lock(&m);

	sleep(3);
	money += 300;
	pthread_cond_broadcast(&v);//唤醒
	pthread_mutex_unlock(&m);
	


	pthread_join(pid1, NULL);
	pthread_join(pid2, NULL);

	//销毁锁、条件变量
	pthread_mutex_destroy(&m);
	pthread_cond_destroy(&v);

	return 0;
}
复制代码

运行结果

image.png

几种线程安全机制的比较:

互斥锁:互斥操作,使用不当容易死锁(一般考虑临界资源)
读写锁:使用和互斥锁基本一致,区别是上锁上读锁和写锁(临界资源比较多的情况)
信号量:P/V操作,只要信号量大于0就可以P
条件变量:互斥锁的一个加强,当某个条件产生的时候,才能做某件事情(不是所有情况下,线程能够通过上互斥锁访问临界资源,而是有一定要求的时候)
复制代码

 

线程池:

1,任务队列中刚开始没有任何任务,是一个具有头结点的空链队列。

2,使用互斥锁来保护这个队列。

3,使用条件变量来代表任务队列中的任务个数的变化——将来如果主线程往队列中投放任务,那么可以通过条件变量来唤醒那些睡着了的线程。

4,通过一个公共开关——shutdown,来控制线程退出,进而销毁整个线程池。
复制代码

 

API接口

线程池相关的结构体

表示一个任务
image.png
线程池
image.png
image.png

1、初始化线程池
image.png

2.添加任务

image.png

3、添加活跃线程

image.png

image.png
4、删除活跃线程

image.png

5、销毁线程池

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享