在linux中进程是系统中程序执行和资源分配的基本单位,每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程在进行切换时操作系统的开销比较大。为了提高效率,操作系统又又引入了另一个概念——线程,也成为轻量级进程。线程可以对进程的内存空间和资源进行访问,并于同一进程中的其它线程共享。因此线程的上下文的切换比进程的开销要小得多。一个进程可以由多个线程,其中每个线程共享该进程所有的资源。要注意的是,由于线程共享了进程的资源域地址空间,因此,任何线程对系统资源的操作都会给其它线程带来影响,所以线程的同步是很重要的问题。
1、线程的基本函数
通常使用pthread_create()函数来创建线程,创建线程时要指定线程的执行函数。线程创建之后就开始执行相应的线程函数。在函数运行完之后,线程结束。
pthread_create()函数的语法要点
所需头文件 | #include <pthread.h> |
函数原型 | int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); Compile and link with -pthread. //编译的时候要加上-pthread后缀 |
函数传入值 | thread:线程标识符 |
attr:线程属性设置,NULL表示缺省属性 | |
start_routine:线程执行函数,参数和返回值都为void* | |
arg:传递给start_routine短地参数 | |
函数返回值 | 成功:0 |
出错:返回错误码 |
退出线程的函数时pthread_exit(),这是线程的主动行为。需要注意的是,在使用线程函数时,不能使用exit()函数。exit()函数时进程推出函数,作用是使当前的进程中止。通常一个进程包含多个线程,如果调用了exit()函数,该进程的所有线程都会结束,因此要使用pthread_exit()函数代替exit()函数结束当前线程。
pthread_exit()函数的语法要点
所需头文件 | #include <pthread.h> |
函数原型 | void pthread_exit(void *retval); Compile and link with -pthread. //编译的时候要加上-pthread后缀 |
函数传入值 | retval:线程结束时的返回值,可通过pthread_join函数来接收 |
进程之间可以使用wait()函数来等待回收子进程,线程之间也有类似机制,那就是pthread_join()函数。这个函数是一个线程阻塞函数,调用它的函数将一直等待到指定的线程结束为止。当函数返回时,表明可以释放已结束线程的相关资源。
pthread_join()函数的语法要点
所需头文件 | #include <pthread.h> |
函数原型 | int pthread_join(pthread_t thread, void **retval); Compile and link with -pthread. //编译的时候要加上-pthread后缀 |
函数传入值 | thread:等待线程的标识符 |
retval:用户定义的指针,用来接收被等待线程结束时的返回值(不为NULL时) | |
函数返回值 | 成功:0 |
失败:返回错误码 |
在某些应用中,经常会使用一个线程前去终止另一个线程可以通过pthread_cancel()函数实现这种功能。当然在被取消的线程的内部先调用pthread_setcancel()函数和pthread_setcanceltype()函数设置相应的取消状态
pthread_cancel()函数的语法要点
所需头文件 | #include <pthread.h> |
函数原型 | int pthread_cancel(pthread_t thread); Compile and link with -pthread. //译的时候要加上-pthread后缀 |
函数传入值 | thread:要取消线程的标识符 |
函数返回值 | 成功:0 |
出错:返回错误码 |
(2)实例代码演示
/*thread.c
该程序中的每个任务完成时间时随机的*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#define THREAD_NUM 3 /*线程数*/
#define REPART_NUM 5 /*每个线程中的循环次数*/
#define DELAY_TIME_LEVELS 6.0 /*循环之间的最大时间间隔*/
void *thrd_fun(void *arg)
{
/*线程函数*/
int thrd_num = (int)arg;
int delay_time = 0;
int count = 0;
printf("Thread %d is starting\n", thrd_num);
for(count = 0; count < REPART_NUM; count++)
{
delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
sleep(delay_time);
printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time);
}
printf("Thread %d finished\n", thrd_num);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int num, res;
pthread_t thread[THREAD_NUM];
void *thrd_ret;
for(num = 0; num < THREAD_NUM; num++)
{
/*创建多线程*/
res = pthread_create(&thread[num], NULL, thrd_fun, (void*)num);
if(res != 0)
{
printf("pthread_create error");
exit(res);
}
}
printf("Create treads success\n Waiting for threads to finish...\n");
for(num = 0; num < THREAD_NUM; num++)
{
/*等待线程结束*/
res = pthread_join(thread[num], &thrd_ret);
if(!res)
{
printf("Thread %d joined\n", num);
}
else
{
printf("Thread %d joined faild\n", num);
}
}
exit(0);
}
运行结果如下所示:
2、线程之间的同步与互斥
由于线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须考虑线程间资源访问的同步与互斥问题,下面介绍互斥锁与信号量。
(1)互斥锁
互斥锁通过简单的加锁方法来保证对共享资源的源自操作。互斥锁只有两种状态:上锁和解锁,可以把互斥锁看成是某种意义上的全集变量。同一时刻只能由一个线程持有某个互斥锁,拥有互斥锁的线程能够对共享资源进行访问。若线程对一个已经被上锁的互斥锁加锁时,该线程就会睡眠,直到其它线程释放掉互斥锁为止。可以说,这把互斥锁保证让每个线程对共享资源顺序进行原子操作。互斥锁机制的基本函数如下:
- 互斥锁初始化:pthread_mutex_init()
- 互斥锁上锁:pthread_mutex_lock()
- 互斥锁判断上锁:pthread_mutex_trylock()
- 互斥锁解锁:pthread_mutex_unlock()
- 消除互斥锁:pthread_mutex_destroy()
注意:在Ubuntu14.04中man手册查不到这些函数,原因时系统自带的man手册并不完全。解决方法如下
sudo apt-get install glibc-doc
sudo apt-get install manpages-posix-dev
pthread_mutex_init语法要点
所需头文件 | #include <pthread.h> |
函数原型 | int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); |
函数传入值 | mutex:互斥锁 |
mutexattr:互斥锁属性,NULL表示缺省属性 | |
函数返回值 | 成功:0 |
出错:返回错误码 |
pthread_mutex_lock()函数语法要点
所需头文件 | #include <pthread.h> |
函数原型 | int pthread_mutex_lock(pthread_mutex_t *mutex); |
函数传入值 | mutex:互斥锁 |
函数返回值 | 成功:0 |
错误:返回错误码 |
其余三个函数语法要点同pthread_mutex_lock(),不做具体介绍
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
使用互斥锁实现线程的同步,使得程序能够有序的执行
/*thread.c
该程序中的每个任务完成时间时随机的*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#define THREAD_NUM 3 /*线程数*/
#define REPART_NUM 5 /*每个线程中的循环次数*/
#define DELAY_TIME_LEVELS 6.0 /*循环之间的最大时间间隔*/
pthread_mutex_t mutex;
void *thrd_fun(void *arg)
{
/*线程函数*/
int thrd_num = (int)arg;
int delay_time = 0;
int count = 0;
int res;
/*互斥锁上锁*/
res = pthread_mutex_lock(&mutex);
if(res)
{
printf("Thread %d lock faild\n", thrd_num);
pthread_exit(NULL);
}
printf("Thread %d is starting\n", thrd_num);
for(count = 0; count < REPART_NUM; count++)
{
delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
sleep(delay_time);
printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time);
}
printf("Thread %d finished\n", thrd_num);
/*互斥锁解锁*/
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int num, res;
pthread_t thread[THREAD_NUM];
void *thrd_ret;
srand(time(NULL));
/*互斥锁初始化*/
pthread_mutex_init(&mutex, NULL);
for(num = 0; num < THREAD_NUM; num++)
{
/*创建多线程*/
res = pthread_create(&thread[num], NULL, thrd_fun, (void*)num);
if(res != 0)
{
printf("pthread_create error");
exit(res);
}
}
printf("Create treads success\n Waiting for threads to finish...\n");
for(num = 0; num < THREAD_NUM; num++)
{
/*等待线程结束*/
res = pthread_join(thread[num], &thrd_ret);
if(!res)
{
printf("Thread %d joined\n", num);
}
else
{
printf("Thread %d joined faild\n", num);
}
}
/*消除互斥锁*/
pthread_mutex_destroy(&mutex);
exit(0);
}
运行结果如下所示:
很明显,使用互斥锁以后线程的执行顺序得到了明显的改善
(2)信号量
信号量实现linux线程的同步与互斥:https://blog.csdn.net/David_361/article/details/86672257