线程创建、死锁与互斥锁


前言

本文主要介绍线程的创建与死锁、互斥锁的问题。


一、什么是线程?

线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,可以看作是Unix进程的表亲。

二、线程的创建

1.线程相关知识

一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程)。所有的线程都是在同一进程空间运行,共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等。

pthread_create():主线程调用创建子线程
pthread_self():获取线程ID

主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。

主线程和子线程通常定义以下两种关系:

1.可会合(joinable)主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。

2.相分离(detached)子线程无需和主线程会合,即相分离的,常用在线程数较多的情况下。

将线程设置为分离状态的两种方法(在默认的情况下,线程是非分离状态的):

  1. 线程里面调用 pthread_detach(pthread_self())
  2. 在创建线程的属性设置里设置PTHREAD_CREATE_DETACHED属性

函数原型:

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

Compile and link with -pthread.

相关说明:

  • thread: 一个pthread_t类型的指针,他用来返回该线程的线程ID。
  • attr: 线程的属性,其类型是pthread_attr_t类型,其定义如下:
typedef struct
{
	int						detachstte;		//线程的分离状态
	int						schedpolicy;	//线程的调度策略
	struct sched_param		schedparam;		//线程的调度参数
	int						inheritsched;	//线程的继承性
	int						scope;			//线程的作用域
	size_t					guardsize;		//线程栈末尾的警戒缓冲区大小
	int						stackaddr_set;
	void *					stackaddr;		//线程栈的位置
	size_t					stacksize;		//线程栈的大小
}pthread_attr_t;
  • start_routine: 一个函数指针,它指向的函数原型是 void *func(void *),是所创建的子线程要执行的任务(函数)
  • arg: 所调用的函数的参数,若有多个参数需要传递给子线程则封装到一个结构体里传进去

2.线程创建示例

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

void *thread_worker1(void *args);		//函数声明
void *thread_worker2(void *args);

int main(int argc, char **argv)
{
	int				shared_var = 1000;
	pthread_t		tid;
	pthread_attr_t	thread_attr;

	if( pthread_attr_init(&thread_attr) )		//线程属性初始化
	{
		printf("pthread_attr_init() failure:%s\n",strerror(errno));
		return -1;
	}

	if( pthread_attr_setstacksize(&thread_attr,120*1024) )		//设置线程栈的大小
	{
		printf("pthread_attr_setstacksize() failure:%s\n",strerror(errno));
		return -1;
	}

	if( pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED) )		//设置线程的分离状态为相分离状态
	{
		printf("pthread_attr_setdetachstate() failure:%s\n",strerror(errno));
		return -1;
	}

	pthread_create(&tid, &thread_attr,thread_worker1,&shared_var);	//创建线程,thread_worker1为线程要执行的函数,&shared_var为传入thread_worker1的参数
	printf("Thread worker1 tid[%ld] created ok\n",tid);

	pthread_create(&tid,NULL,thread_worker2,&shared_var);	//创建线程,此线程为默认的可会合状态
	printf("Thread worker2 tid[%ld] created ok\n",tid);

	pthread_attr_destroy(&thread_attr);		//摧毁释放设置的线程属性

	/* Wait until thread worker2 exit() */
	pthread_join(tid,NULL);		//主线程调用pthread_join等待可会合状态的线程退出

	while(1)
	{
		printf("Main/Control thread shared_var:%d\n",shared_var);
		sleep(10);
	}
}

void *thread_worker1(void *args)	//子线程1执行的函数
{
	 int		*ptr = (int *)args;

	 if( !args )
	 {
		printf("%s() get invalid arguments\n",__FUNCTION__);
		pthread_exit(NULL);
	 }

	 printf("Thread worker 1 [%ld] start running...\n",pthread_self());

	 while(1)	//让子线程1一直运行
	 {
	 	printf("+++:%s before shared_var++:%d\n",__FUNCTION__,*ptr);	//打印shared_var的值
		*ptr += 1;	//shared_var值加1
		sleep(2);	//睡眠2s
		printf("+++:%s after sleep shared_var:%d\n",__FUNCTION__,*ptr);	//再次打印shared_var的值
	 }

	 printf("Thread worker 1 exit...\n");

	 return NULL;
}

void *thread_worker2(void *args)	//子线程2执行的函数
{
	int		*ptr = (int *)args;

	if( !args )
	{
		printf("%s() get invalid arguments\n",__FUNCTION__);
		pthread_exit(NULL);
	}

	printf("Thread worker 2 [%ld] start running...",pthread_self());

	while(1)	//让子线程2一直运行
	{
		printf("---:%s before shared_var++:%d\n",__FUNCTION__,*ptr);	//打印shared_var的值
		*ptr += 1;	//shared_var的值加1
		sleep(2);	//睡眠2s
		printf("---:%s after sleep shared_var:%d\n",__FUNCTION__,*ptr);	//再次打印shared_var的值
	}

	printf("Thread worker 2 exit...\n");

	return NULL;
}

程序运行结果如下:
在这里插入图片描述

三、互斥锁与死锁

两个或多个线程需要使用同一资源便会产生互斥现象

如果多个线程要调用多个对象,则在上锁的时候可能会出现“死锁”。

死锁产生的4个必要条件:

  • 互斥 :某种资源一次只允许一个进程访问
  • 占有且等待 :一个进程本身占有资源,同时还有资源未满足
  • 不可抢占 :其他进程已占有
  • 循环等待 :存在一个进程链,每个进程都占有下一个进程所需的至少一种资源

只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享资源所必须的,应加以保证,所以,主要是破坏产生死锁的其他三个条件。

代码如下(示例):

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

 void *thread_worker1(void *args);
 void *thread_worker2(void *args);

 typedef struct worker_ctx_s	//以结构体封装要传递的共享变量shared_var和它相应的互斥锁lock
 {
 		int shared_var;
 		pthread_mutex_t lock;
 } worker_ctx_t;

 int main(int argc, char **argv)
 {
 		worker_ctx_t 		worker_ctx;
 		pthread_t 			tid;
 		pthread_attr_t 		thread_attr;

 		worker_ctx.shared_var = 1000;
 		pthread_mutex_init(&worker_ctx.lock, NULL);		//初始化互斥锁


 		if( pthread_attr_init(&thread_attr) )
 		{
 			printf("pthread_attr_init() failure: %s\n", strerror(errno));
 			return -1;
 		}

 		if( pthread_attr_setstacksize(&thread_attr, 120*1024) )		//设置线程栈大小
 		{
 			printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
 			return -1;
 		}

 		if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )		//设置线程分离状态为相分离
 		{
 			printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
 			return -1;
 		}
 		pthread_create(&tid, &thread_attr, thread_worker1, &worker_ctx);	//创建子线程1
 		printf("Thread worker1 tid[%ld] created ok\n", tid);

 		pthread_create(&tid, &thread_attr, thread_worker2, &worker_ctx);	//创建子线程2
 		printf("Thread worker2 tid[%ld] created ok\n", tid);

 		while(1)	//两子线程均为相分离状态,主线程可运行到此
 		{
 			printf("Main/Control thread shared_var: %d\n", worker_ctx.shared_var);
 			sleep(10);
 		}

 		pthread_mutex_destroy(&worker_ctx.lock);	//摧毁互斥锁
 }

 void *thread_worker1(void *args)		//子线程1执行函数
 {
 		worker_ctx_t *ctx = (worker_ctx_t *)args;

 		if( !args )
 		{
 			printf("%s() get invalid arguments\n", __FUNCTION__);
 			pthread_exit(NULL);
 		}

 		printf("Thread workder 1 [%ld] start running...\n", pthread_self());

 		while(1)
 		{
 			pthread_mutex_lock(&ctx->lock);		//申请锁,阻塞锁,锁被别的线程持有则函数不会返回

 			printf("+++: %s before shared_var++: %d\n", __FUNCTION__, ctx->shared_var);
 			ctx->shared_var ++;
 			sleep(2);
 			printf("+++: %s after sleep shared_var: %d\n", __FUNCTION__, ctx->shared_var);

 			pthread_mutex_unlock(&ctx->lock);	//释放锁,让其他线程可访问shared_var

 			sleep(1);	//延时,否则一个线程拿到锁之后会一直占有该锁,而另一个线程则不能获取到锁
 		}

 		printf("Thread workder 1 exit...\n");

 		return NULL;
 }

 void *thread_worker2(void *args)
 {
 		worker_ctx_t *ctx = (worker_ctx_t *)args;

 		if( !args )
 		{
 			printf("%s() get invalid arguments\n", __FUNCTION__);
 			pthread_exit(NULL);
 		}

	 	printf("Thread workder 2 [%ld] start running...\n", pthread_self());

	 	while(1)
	 	{
	 		if(0 != pthread_mutex_trylock(&ctx->lock) )		//申请锁,非阻塞锁,锁被别的线程占用则返回非0值,没有被占用则返回0
			{
				continue;
	 		}

	 		printf("---: %s before shared_var++: %d\n", __FUNCTION__, ctx->shared_var);
	 		ctx->shared_var ++;
	 		sleep(2);
	 		printf("---: %s after sleep shared_var: %d\n", __FUNCTION__, ctx->shared_var);

	 		pthread_mutex_unlock(&ctx->lock);	//释放锁

	 		sleep(1);	//延时,否则一个线程拿到锁之后会一直占有该锁,另外一个线程则不能获取到锁
	 	}

	 	printf("Thread workder 2 exit...\n");

	 	return NULL;
	 }

程序运行结果如下:
在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值