Linux线程编程

在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

猜你喜欢

转载自blog.csdn.net/David_361/article/details/86639865
今日推荐