前言
本文主要介绍线程的创建与死锁、互斥锁的问题。
一、什么是线程?
线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,可以看作是Unix进程的表亲。
二、线程的创建
1.线程相关知识
一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程)。所有的线程都是在同一进程空间运行,共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等。
pthread_create():主线程调用创建子线程
pthread_self():获取线程ID
主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。
主线程和子线程通常定义以下两种关系:
1.可会合(joinable):主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
2.相分离(detached):子线程无需和主线程会合,即相分离的,常用在线程数较多的情况下。
将线程设置为分离状态的两种方法(在默认的情况下,线程是非分离状态的):
- 线程里面调用 pthread_detach(pthread_self())
- 在创建线程的属性设置里设置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;
}
程序运行结果如下: