进程和线程

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

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

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


多线程例子

        使用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);  } 

     

代码一行行解释。

#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);  } 



         在上面的代码中,将两个向量的点乘分别放到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