Linux操作系统分析(九)- 多线程和线程安全

Linux操作系统分析(9)- 多线程和线程安全

进程和线程

         进程是操作系统的概念,每当我们执行一个程序时,对于操作系统来讲就创建了一个进程,在这个过程中,伴随着资源的分配和释放。可以认为进程是一个程序的一次执行过程。

         线程是进程内的一个相对独立的可执行的单元。若把进程称为任务的话,那么线程则是应用中的一个子任务的执行。

        线程和进程十分相似,不同的只是线程比进程小。首先,线程采用了多个线程可共享资源的设计思想;例如,它们的操作大部分都是在同一地址空间进行的。其次,从一个线程切换到另一线程所花费的代价比进程低。再次,进程本身的信息在内存中占用的空间比线程大,因此线程更能允分地利用内存。


多线程例子

        使用Pthreads的主要动机是提高潜在程序的性能。 当与创建和管理进程的花费相比,线程可以使用操作系统较少的开销,管理线程需要较少的系统资源。

        Pthreads定义了一套C语言的类型、函数与常量,它以pthread.h头文件和一个线程库实现。

      

#include <pthread.h> 
#include <stdio.h> 
#define NUM_THREADS     5 
 
void *PrintHello(void *threadid) 
{ 
   int tid; 
   tid = (int)threadid; 
   printf("Hello World! It's me, thread #%d!\n", tid); 
   pthread_exit(NULL); 
} 
 
int main (int argc, char *argv[]) 
{ 
   pthread_t threads[NUM_THREADS]; 
   int rc, t; 
   for(t=0; t<NUM_THREADS; t++){ 
      printf("In main: creating thread %d\n", t); 
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); 
      if (rc){ 
         printf("ERROR; return code from pthread_create() is %d\n", rc); 
         return -1; 
      } 
   } 
   pthread_exit(NULL); 
} 

Linux操作系统分析(九)- 多线程和线程安全     

代码一行行解释。

#1 包含pthread头文件,这个在linux中默认就有,无需配置安装。

#5 -11 定义线程函数,作为后面pthread_create的参数,pthread_exit()为终止当前线程。

#15 定义一个线程句柄数组。

#17-24 创建5个线程。

pthread_create参数:
thread:返回一个不透明的,唯一的新线程标识符。
attr:不透明的线程属性对象。可以指定一个线程属性对象,或者NULL为缺省值。
start_routine:线程将会执行一次的C函数。
arg: 传递给start_routine单个参数,传递时必须转换成指向void的指针类型。没有参数传递时,可设置为NULL。


pthread_create()函数允许程序员想线程的start routine传递一个参数。当多个参数需要被传递时,可以通过定义一个结构体包含所有要传的参数,然后用pthread_create()传递一个指向改结构体的指针,来打破传递参数的个数的限制。
所有参数都应该传引用传递并转化成(void*)。


线程安全

线程安全问题都是由全局变量及静态变量引起的。

若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

看一个向量点乘的例子:


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

#define NUM_THREADS  5 
#define VECLEN 100

typedef struct
{
	double *a;
	double *b;
	double sum;
	int veclen;
}DotData;

pthread_t threads[NUM_THREADS];
pthread_mutex_t mutexsum;
DotData dotstr;
 
void *dotproduct(void *arg) 
{ 
	int i,start,end,offset,len;
	double mysum, *x, *y;
	offset = (int)arg;
	
	len = dotstr.veclen;
	start = offset*len;
	end = start + len;
	x = dotstr.a;
	y = dotstr.b;
	
	mysum = 0;
	for(i=start; i<end; i++)
	{
		mysum += (x[i] * y[i]);
	}
	pthread_mutex_lock(&mutexsum);
	dotstr.sum += mysum;
	printf("mysum%d:%f\n",(int)arg,mysum);
	pthread_mutex_unlock(&mutexsum);
	
	pthread_exit(NULL);

} 
 
int main (int argc, char *argv[]) 
{ 
	int i;
	double *a,*b;
	void *status;
	pthread_attr_t attr;
	
	a = (double*)malloc(NUM_THREADS*VECLEN*sizeof(double));
	b = (double*)malloc(NUM_THREADS*VECLEN*sizeof(double));
	
	for(i=0; i<NUM_THREADS*VECLEN; i++)
	{
		a[i] = 2.0;
		b[i] = a[i];
	}
	
	dotstr.veclen = VECLEN;
	dotstr.a = a;
	dotstr.b = b;
	dotstr.sum = 0;
	pthread_mutex_init(&mutexsum,NULL);
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
	for(i=0; i<NUM_THREADS; i++)
	{
		pthread_create(&threads[i],&attr,dotproduct,(void*)i);
	}
	pthread_attr_destroy(&attr);
	
	for(i=0; i<NUM_THREADS; i++)
	{
		pthread_join(threads[i],&status);
	}
	
	printf("Sum = %f\n",dotstr.sum);
	free(a);
	free(b);
	pthread_mutex_destroy(&mutexsum);
   	pthread_exit(NULL); 
} 

Linux操作系统分析(九)- 多线程和线程安全


         在上面的代码中,将两个向量的点乘分别放到5个线程里分块完成,理想的情况下,计算速度提升了5倍。

        在dotproduct函数中,操作dotstr的时候,使用了互斥锁。首先开启互斥 pthread_mutex_lock(&mutexsum),然后操作数据,最后打开互斥pthread_mutex_unlock(&mutexsum)。虽然在这里添不添加互斥没有关系,但如果对全局变量的操作更加复杂的时候,比如有乘除法的时候,互斥锁就变得很重要了。

        在main函数中,pthread_attr_t表示线程属性结构体,使用的时候需要对此结构体进行初始化,初始化后使用,使用后还要进行去除初始化。pthread_attr_init:初始化,pthread_attr_destory:去除初始化。

        设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。

            pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。


参考

pthreads 的基本用法 - http://www.ibm.com/developerworks/cn/linux/l-pthred/

POSIX 多线程程序设计  - http://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html