多线程相关函数归纳

多线程相关函数必备头文件:

#include <pthread.h>
#include <semaphore.h>

Linux 的线程是通过用户级的函数库实现的,一般采用 pthread 线程库实现线程的访问和控制。它用第 3 方posix 标准的 pthread,具有良好的可移植性。 所以,编译的时候要在后面加上 -lpthread。
比如,编译client.c生成client可执行文件:

gcc -o client client.c -lpthread

1.线程

1.1线程的创建

int pthread_create(pthread_t* thread, pthread_attr_t * attr, void *(*start_routine)(void *), void * arg);

功能: 用来创建线程。
参数 :
thread :传出参数,保存新线程的标识;
attr :一个结构体指针,结构中的元素分别指定新线程的运行属性;但通常传入为 NULL 即可。
start_routine:是一个函数指针,指向新线程的入口点函数(执行线程的时候要执行的函数名)。
arg:用于传递给第 3 个参数指向的入口点函数的参数,可以为 NULL,表示不传递。
返回值:成功,则返回新线程的标识;失败,则返回-1。
1.2线程退出

void pthread_exit(void *retval);

作用;表示线程的退出。其参数可以被其它线程用 pthread_join 函数捕获。
参数:
retval:作为线程结束的返回值,会被后面的pthread_join函数捕获。可以为NULL,或者其他数字。
1.3线程的等待

int pthread_join(pthread_t pthid, void **thread_return);

函数功能: 用于将当前线程挂起,等待某个线程的结束
参数:
Pthid:是某一个线程的ID。
thread_return:是一个传出参数,接收线程函数的返回值。
返回值:获取成功,返回0。失败, 返回负数。

例子:创建一个进程,并且结束它。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/stat.h>

void * fun(void *p) 		
{
 		int i;
 		for(i = 1; i<10; i++)
 		{
 		 	printf("world,p = %d \n", p);
 			sleep(1);
 			if(i=8)
 			{
 				pthread_exit(200);//循环8次时,创建的进程会退出,如果没有这个函数,该进程会在10个循环后自然退出
			}
 		}
}

int main()
{
 	pthread_t id;
 	int a, i;
 	pthread_create(&id, NULL,fun,(void *) 123);//创建一个子线程,该线程函数名为fun,传入的参数为123.
 	for(i = 1; i<10; i++)
 	{
 		printf("hello \n");//这个是一个父进程,表示与fun这个子线程同时进行的。
 		sleep(1);
 	}
 	pthread_join(id, &a);//如果父线程的运行次数比子线程的要少
						//当父线程结束的时候,如果没有 pthread_join函数等待子线程执行的话,父线程会退出,而主线程的退出会导致进程的退出,故子线程也会退出。
 	printf("a = %d\n", a);
 	return 0;
}

2.线程的互斥锁

2.1创建和销毁互斥锁
创建互斥锁有2种方式:
1)静态方式:使用相应的宏来初始化互斥锁;
POSIX 定义了一个宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2)动态方式:使用相应的函数来初始化互斥锁;

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

功能: 初始化互斥锁。
参数:
Mutex:互斥锁结构。
Mutexattr:用于指定互斥锁属性(类型),通常为 NULL标识使用缺省属性。
返回值:成功返回0,失败返回负数。

2.2注销互斥锁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:注销一个互斥锁
参数:
Mutex:要注销的互斥锁结构
返回值:成功返回0,失败返回负数。
特别说明:销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。

2.3锁的种类

typedef struct
{
 	int __mutexkind;
} pthread_mutexattr_t;
int __mutexkind锁的类型描述
PTHREAD_MUTEX_TIMED_NP(NULL)普通锁(快速锁)当一个线程加锁以后,其余请求锁的线程将形成一个阻塞等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性
PTHREAD_MUTEX_RECURSIVE_NP嵌套锁(递归锁)允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
PTHREAD_MUTEX_ERRORCHECK_NP检错锁如果同一个线程请求同一个锁,则返回 EDEADLK。否则与 PTHREAD_MUTEX_TIMED_NP 类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。

例子:初始化为检错锁

pthread_mutex_t lock;
pthread_mutexattr_t mutexattr={PTHREAD_MUTEX_ERRORCHECK_NP};
pthread_mutex_init(&lock, &mutexattr);

2.4锁的操作
1)加锁

int pthread_mutex_lock(pthread_mutex_t *mutex)

函数功能:给互斥锁加锁
参数:
Mutex:要加锁的互斥锁结构
返回值:成功返回0,否则返回负数
说明:对于普通锁,解锁者可以是同进程内任何线程;而检错锁和嵌套锁则必须由加锁者解锁才有效,否则返回 EPERM;在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

2)解锁

int pthread_mutex_unlock(pthread_mutex_t *mutex)

函数功能:给互斥锁解锁
参数:
Mutex:要解锁的互斥锁。
返回值:成功返回0,否则返回负数
说明:根据不同的锁类型,可实现不同的行为:
对于快速锁, pthread_mutex_unlock 解除锁定;
对于递规锁, pthread_mutex_unlock 使锁上的引用计数减 1;
对于检错锁,如果锁是本线程锁定的,则解除锁定,否则什么也不做。

3)测试加锁

int pthread_mutex_trylock(pthread_mutex_t *mutex)

函数功能:给一个互斥锁测试加锁。与 pthread_mutex_lock()类似,如果互斥锁未被上锁则对其上锁,但不同的是在锁已经被占据时返回 EBUSY 而不是挂起等待。
参数:
Mutex:要测试加锁的互斥锁结构
返回值:成功返回0,否则返回负数

3)销毁锁

int pthread_mutex_destroy(pthread_mutex_t *mutex)

函数功能:销毁互斥锁,回到初始化锁前的状态。
参数:
Mutex:要测试加锁的互斥锁结构
返回值:成功返回0,否则返回负数

例子:火车售票并且对票进行加锁

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

int ticketcount = 11;//票数为11
pthread_mutex_t lock;//1:定义一个全局的 pthread_mutex_t lock; 

void* salewinds1(void* args)
{
 	while(1)
 	{
 		pthread_mutex_lock(&lock); 	//3:在子线程函数中调用 pthread_mutex_lock 加锁。
 		if(ticketcount > 0){ 			//如果有票
 			printf("windows1 start sale ticket!the ticket is:%d\n", ticketcount);
 			sleep(2);
 			ticketcount --;
 			printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
 		}
 		else
 		{
 			pthread_mutex_unlock(&lock);//4:在子线程函数中调用 pthread_mutex_unlock 解锁。
 			pthread_exit(NULL);//退出子线程
 		}
 		pthread_mutex_unlock(&lock);
 		sleep(1); //要放到锁的外面,让另一个有时间锁
 	}
}

void* salewinds2(void* args)
{
 	while(1)
 	{
 		pthread_mutex_lock(&lock);//3:在子线程函数中调用 pthread_mutex_lock 加锁。
 		if(ticketcount>0)
 		{
 			printf("windows2 start sale ticket!the ticket is:%d\n",ticketcount);
 			sleep(2);
 			ticketcount --;
 			printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
 		}
 	 	else
 	 	{
 			pthread_mutex_unlock(&lock);//4:在子线程函数中调用 pthread_mutex_unlock 解锁。
 			pthread_exit(NULL);//退出子线程
 		}
 		pthread_mutex_unlock(&lock);
 		sleep(1);
 	}
}

int main()
{
 		pthread_t pthid1 = 0;
 		pthread_t pthid2 = 0;
 		pthread_mutex_init(&lock,NULL); //2:初始化一个互斥锁,其处于被解锁的状态,可以被上锁。
 		pthread_create(&pthid1,NULL,salewinds1,NULL);//创建售票线程1
 		pthread_create(&pthid2,NULL,salewinds2,NULL);//创建售票线程2
 		pthread_join(pthid1,NULL);//等待线程1,以免主线程跑完出去了导致子线程自动结束
 		pthread_join(pthid2,NULL);//等待线程2,以免主线程跑完出去了导致子线程自动结束
 		pthread_mutex_destroy(&lock); 			//5:销毁锁
 		return 0;
}

3.线程的条件变量

3.1条件变量的创建和注销
1)静态创建方式

使 PTHREAD_COND_INITIALIZER 常量,如下:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

2)动态创建方式

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

功能:动态初始化条件变量
参数:
Cond:条件变量结构
cond_attr:通常为NULL。
返回值:成功返回0,失败返回-1
3)条件变量注销

int pthread_cond_destroy(pthread_cond_t *cond);

功能:注销一个条件变量
参数:
Cond:要注销的条件变量结构。
返回值:成功返回0,失败返回-EBUSY
说明:只有在没有线程在该条件变量上等待的时候才能注销这个条件变量。
4)无条件等待函数

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

功能:线程解开mutex指向的锁并被条件变量cond阻塞,无条件的等待cond信号的出现就会返回。
参数:
Cond:要等待的条件变量
Mutex:相对应的互斥锁
返回值 成功返回0,失败返回负数。

5)计时等待函数

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

功能:线程解开mutex指向的锁并被条件变量cond阻塞,计时等待cond信号的出现就会返回。经历 abstime 段时间后,即使条件变量不满足,阻塞也被解除。
参数:
Cond:要等待的条件变量
Mutex:相对应的互斥锁
Abstime:与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
返回值:成功返回0,超时返回ETIMEOUT,失败返回负数。

6)条件变量激发
激发条件有两种形式:

int pthread_cond_signal(pthread_cond_t *cond)//按入队顺序激活一个等待该条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond)//激活所有等待线程。

功能:线程解开mutex指向的锁并被条件变量cond阻塞,计时等待cond信号的出现就会返回。经历 abstime 段时间后,即使条件变量不满足,阻塞也被解除。
参数:
Cond:要等待的条件变量
返回值:成功返回0,超时返回ETIMEOUT,失败返回负数。

例子:

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

pthread_mutex_t mutex; 	//定义了一个互斥锁变量
pthread_cond_t cond; 		//定义了一个条件变量

void * child1(void *arg)
{
 	printf("线程 1 开始运行! \n");
 	printf("线程 1 上锁了: %d\n", pthread_mutex_lock(&mutex));//设置互斥锁为上锁
 	printf("线程 1 开始等待条件被激活\n");
 	pthread_cond_wait(&cond,&mutex);  		//等待父线程发送信号
 	printf("条件满足后,停止阻塞! \n");
 		pthread_mutex_unlock(&mutex); 			//解锁
 	return 0;
}

int main(void)
{
 	pthread_t tid1;

 	printf("开始测试条件变量! \n");
 	pthread_mutex_init(&mutex,NULL);//初始化互斥锁,普通锁
 	pthread_cond_init(&cond,NULL);//初始化条件变量

 	pthread_create(&tid1,NULL,child1,NULL);//创建线程child1
 	sleep(5); //延迟
 	pthread_cond_signal(&cond); 		//激活条件,结束等待
 	sleep(3);								//调度子线程
 	pthread_cond_destroy(&cond); 	//注销条件变量
 	pthread_mutex_destroy(&mutex);	//注销互斥锁变量
 	return 0;
}

4.线程的信号灯

1)信号灯的创建

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

功能:创建信号灯。
Sem:这个是个传出值,传出信号灯的标识,用于以下点灯、灭灯操作。
pshared :是否为多进程共享而不仅仅是用于一个进程之间的多线程共享。通常为 0.表示线程间。
value :为信号灯的初值。
成功返回0,失败返回负数。

2)信号灯的注销

int sem_destroy(sem_t * sem);

功能 注销一个信号灯。
参数:
Sem:要注销信号灯的标识。
返回值:成功返回0,失败返回负数。

3)点灯
int sem_post(sem_t * sem); //相当于解锁
功能:将信号灯值加 1,表示增加一个可访问的资源。信号灯值大于 0,才能访问公共资源。
参数 Sem:要操作的信号灯
返回值:成功返回0,否则返回负数
4)灭灯
int sem_wait(sem_t * sem); //相当于加锁
int sem_trywait(sem_t * sem);
功能:
sem_wait:主要用来阻塞当前线程,等待灯亮(信号灯值大于 0),然后将信号灯原子地减 1,并返回。
sem_trywait:为 sem_wait的非阻塞版,如果信号灯计数大于 0,则原子地减 1 并返回 0,否则立即返回-1,
参数:Sem:要操作信号灯的标识.
返回值:成功返回0,否则返回负数
5)获取灯值

int sem_getvalue(sem_t * sem, int * sval);

功能:获取信号灯的值。
参数:
Sem:要获取的信号灯。
Sval:将值存放在这个指针所致的地址下。
返回值:成功获取返回0,否则返回负数。
例子:

#include<pthread.h>
#include<stdio.h>
#include <semaphore.h> //头文件包含

int ticketcount = 10;
sem_t lock;

void *chk1(void *args)
{
 	while(1){
 		sem_wait(&lock); //因为要访问全局的共享变量 ticketcount,所以就要加锁
 		if(ticketcount > 0){ 	  	//如果有票
 			printf("windows1 start sale ticket!the ticket is:%d\n",
                    ticketcount);
 			ticketcount --; 		//则卖出一张票
 			sleep(3);
 			printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
 		}
 		else { 		 				//如果没有票了,就解锁退出
 			sem_post(&lock);
 				break;
 		}
 		 	sem_post(&lock);
 		 	sleep(1);  				//要放到锁的外面
 		}
 		pthread_exit(NULL);
 	}

void *chk2(void *args)
{
 	while(1){
 		sem_wait(&lock); //因为要访问全局的共享变量 ticketcount,所以就要加锁
 		if(ticketcount > 0){ 	//如果有票
 			printf("windows2 start sale ticket!the ticket is:%d\n",
                   ticketcount);
 			ticketcount --;		//则卖出一张票
 			sleep(3);
 			printf("sale ticket finish!,the last ticket is:%d\n",ticketcount);
 		}
 		else{ 						//如果没有票了,就解锁退出
 			sem_post(&lock);
 				break;
 		}
 		sem_post(&lock);
 			sleep(1);  				//要放到锁的外面
 	}
 	pthread_exit(NULL);
}

main()
{
 	pthread_t pthid1,pthid2;
 	sem_init(&lock,0,1);  				//信号灯值初始为 1,表示资源可用
 	pthread_create(&pthid1,NULL,chk1,NULL);
 	pthread_create(&pthid2,NULL,chk2,NULL);
 	pthread_join(pthid1,NULL);
 	pthread_join(pthid2,NULL);
 	sem_destroy(&lock);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小坚学Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值