• Linux程序设计学习笔记----多线程编程基础概念与基本操作


    转载请注明出处,http://blog.csdn.net/suool/article/details/38542543,谢谢.

    基本概念

    线程和进程的对照

    用户空间资源对照

    每一个进程在创建的时候都申请了新的内存空间以存储代码段数据段BSS段堆栈空间,而且这些的空间的初始化值是父进程空间的,父子进程在创建后不能互訪资源.

    而每一个新创建的线程则只申请了自己的栈,空间,与同进程的其它线程共享该进程的其它数据空间包含代码段数据段BSS段堆以及打开的库,mmap映射的文件与共享的空间,使得同进程下的线程共享数据十分的方便,只需借助这些共享区域就可以,但也有问题即是同步问题.

    内核空间资源对照

    从上面的用户空间对照来看,能够非常easy的区分线程和进程,可是,却有非常多人说:linux并不区分进程和线程.说这些话的人就是站在内核空间资源的角度说的.

    在之前的笔记中说过,在创建一个进程后,会对应的在内核空间建立一个该进程的PCB来标示该进程的相关的信息.

    而眼下linux的线程也被称为轻量级进程.就是由于在创建线程的时候,Linux内核依旧会创建一个新的PCB来标示这个线程,而内核对进程/线程的认识来源于PCB,因此对于内核来说,差点儿没有进程和线程之分.但在用户空间来看,有分别.

    并且在Linux系统下,每一个进程的CPB中的struct mm_struct用来描写叙述这个进程的地址空间,使用fork创建的新进程与父进程的的地址空间是分开的,而同一个进程下创建的线程共享这一空间,从调度的角度看,OS是急于线程的调度,因此内核并不区分二者.

    一个进程假设不创建新的线程,能够说他是一个仅仅有一个线程的进程,假设创建了新的线程,则说原来的进程位为主线程.

    优劣对照

    通过之前的解说可知,进程使用的时候占用了大量的系统内存空间,特别是在进行进程间通信的时候必须借助os进行,这使得进程的使用耗费资源并且不够灵活;而线程则使用资源少,使用灵活,同进程下线程通信不须要借助os,非常多的app都是大量使用线程非常少使用多进程.当然线程不能脱离进程而存在.

    关系

    线程是进程的一个运行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

    一个进程由几个线程组成(拥有非常多相对独立的运行流的用户程序共享应用程序的大部分数据结构)。线程与同属一个进程的其它的线程共享进程所拥有的所有资源。

    "进程——资源分配的最小单位。线程——程序运行的最小单位"

    进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响。而线程仅仅是一个进程中的不同运行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮。但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同一时候进行而且又要共享某些变量的并发操作,仅仅能用线程。不能用进程。

    因此简单的总结使用线程的理由例如以下:

    使用多线程的理由之中的一个是和进程相比。它是一种很"节俭"的多任务操作方式。我们知道,在Linux系统下。启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而执行于一个进程中的多个线程,它们彼此之间使用同样的地址空间。共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间。并且。线程间彼此切换所需的时间也远远小于进程间切换所须要的时间。

    据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右。当然。在详细的系统上。这个数据可能会有较大的差别。

    使用多线程的理由之二是线程间方便的通信机制

    对不同进程来说,它们具有独立的数据空间,要进行数据的传递仅仅能通过通信的方式进行,这样的方式不仅费时,并且非常不方便。

    线程则不然,因为同一进程下的线程之间共享数据空间,所以一个线程的数据能够直接为其他线程所用。这不仅快捷,并且方便。当然,数据的共享也带来其他一些问题,有的变量不能同一时候被两个线程所改动,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击。这些正是编写多线程程序时最须要注意的地方。

    除了以上所说的长处外,不和进程比較。多线程程序作为一种多任务、并发的工作方式,当然有下面的长处:

    • 提高应用程序响应。

      这对图形界面的程序尤其有意义。当一个操作耗时非常长时。整个系统都会等待这个操作。此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,能够避免这种尴尬的情况。

    • 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程执行于不同的CPU上。

    • 改善程序结构。一个既长又复杂的进程能够考虑分为多个线程,成为几个独立或半独立的执行部分。这种程序会利于理解和改动。

    线程的基本操作

    创建进程

    使用函数pthread_create()创建新线程.

    int pthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
    用于创建一个线程,成功返回0。否则返回Exxx(为正数)。

    • pthread_t *tid:线程id的类型为pthread_t,通常为无符号整型,当调用pthread_create成功时,通过*tid指针返回,假设设置为NULL,则不会返回生产的线程的标示符。
    • const pthread_attr_t *attr:指定创建线程的属性,如线程优先级、初始栈大小、是否为守护进程等。能够使用NULL来使用默认值,通常情况下我们都是使用默认值。

    • void *(*func) (void *):函数指针func,线程运行的代码起始地址,指定当新的线程创建之后。将运行的函数。

    • void *arg:线程将运行的函数的參数。假设想传递多个參数,请将它们封装在一个结构体中。

    两个演示样例程序

    /*************************************************************************
    > File Name: pthread_create_exp.c
    > Author:SuooL 
    > Mail:1020935219@qq.com || hu1020935219@gmail.com
    > Website:http://blog.csdn.net/suool | http://suool.net
    > Created Time: 2014年08月13日 星期三 22时07分13秒
    > Description: 创建线程演示样例
    ************************************************************************/
    
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/syscall.h> 
    
    // 全局结构变量
    struct message
    {
    	int i;
    	int j;
    };
    
    
    void *hello(struct message *str)     // 执行函数
    {
    	printf("child,the tid=%lu,pid=%ld
    ",pthread_self(),syscall(SYS_gettid));
    	printf("the arg.i is %d,arg.j is %d
    ",str->i,str->j);
    	while(1);
    }
    
    int main(int argc,char *agrv[])
    {
    	struct message test;
    	pthread_t thread_id;
    	test.i=10;
    	test.j=20;
    	pthread_create(&thread_id,NULL,(void *)*hello,&test);
    	printf("parent,the tid=%lu,pid=%ld
    ",pthread_self(),syscall(SYS_gettid));
    	pthread_join(thread_id,NULL);
    }
    执行结果:(注意编译的时候加上连接库 -lpthread)


    在另外一个终端下查看他们属于一个进程组.

    并将二者的进程用户空间信息输出,发现一样,不过进程号不同.

    面的演示样例程序展示了线程之间的执行关系:

    /*************************************************************************
    > File Name: pthread_exp.c
    > Author:SuooL 
    > Mail:1020935219@qq.com || hu1020935219@gmail.com
    > Website:http://blog.csdn.net/suool | http://suool.net
    > Created Time: 2014年08月13日 星期三 21时48分45秒
    > Description: 
    ************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
    
    void thread(void)    // 线程运行函数
    {
        int i;
        for(i=0;i<3;i++)
        printf("This is a pthread.
    ");
    }
    
    int main(void)
    {
        pthread_t id;
        int i,ret;
        ret=pthread_create(&id,NULL,(void *) thread,NULL);    //创建线程
        if(ret!=0)
        {
            perror("pthread_create");
            exit(EXIT_FAILURE);	
        }
        for(i=0;i<3;i++) 
        printf("This is the main process.
    ");
        pthread_join(id,NULL);
        return 0;
    }

    执行结果例如以下:这是他们争夺cpu的结果

    线程的退出与等待

    线程的退出操作

    新创建的线程从运行用户定义的函数開始运行,指导以下的情况结束:

    • 调用pthread_exit函数退出
    • 其它线程调用pthread_cancel函数取消该线程,且该线程能够被取消
    • 创建线程的进程退出或者整个函数结束
    • 当中一个线程运行了exec系列函数运行新的代码,替换当前的进程的地址空间
    • 当前线程代码运行完成

    退出函数声明例如以下:

    extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
      唯一的參数是函数的返回代码,仅仅要pthread_join中的第二个參数thread_return不是NULL,这个值将被传递给thread_return。最后要说明的是,一个线程不能被多个线程等待。否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。

    等待线程

    为了有效的同步线程,主线程中都将等待子线程结束,显示等待某线程结束能够调用pthread_join函数,类似于进程的wait函数表,声明例如以下:

    extern int pthread_join __P ((pthread_t __th, void **__thread_return));
      第一个參数为被等待的线程标识符,此进程必须同调用它的进程相联系,不能是孤立的线程.假设要设置某个线程为孤立进程,则能够调用pthread_datach()函数.第二个參数为一个用户定义的指针。它能够用来存储被等待线程的返回值。这个函数是一个线程堵塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。

    退出线程演示样例

    以下的代码用于验证线程退出时的全局变量局部变量堆空间怎样管理.

    /*************************************************************************
    > File Name: pthread_exit.c
    > Author:SuooL 
    > Mail:1020935219@qq.com || hu1020935219@gmail.com
    > Website:http://blog.csdn.net/suool | http://suool.net
    > Created Time: 2014年08月13日 星期三 23时04分13秒
    > Description:  线程退出演示样例
    ************************************************************************/
    
    /*
    首先在主线程中创建一个新的线程,在新线程中申请一段堆空间并赋值,然后作为该线程的返回值.
    在主线程中,等待子线程结束,并存储其退出状态值.
    在子线程中申请的堆空间在主线程中也能够释放并訪问,说明子线程在退出时只释放其私有的栈空间.
    显然位于数据段的全局数据是不会再线程退出时释放的们唯独当进程退出时才会释放.
    代码例如以下
    */
    
    
    
    #include<pthread.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    void *helloworld(char *argc);
    int main(int argc,int argv[])
    {
    	int error;
    	int *temptr;
    	
    	pthread_t thread_id;
    	
    	pthread_create(&thread_id,NULL,(void *)*helloworld,"helloworld");
    	printf("*p=%x,p=%x
    ",*helloworld,helloworld);
    	if(error=pthread_join(thread_id,(void **)&temptr))
    	{
    		perror("pthread_join");
    		exit(EXIT_FAILURE);	
    	}
    	printf("temp=%x,*temp=%c
    ",temptr,*temptr);
    	*temptr='d';
    	printf("%c
    ",*temptr);
    	free(temptr);
    	return 0;
    }
    
    void *helloworld(char *argc)
    {
    	int *p;
    	p=(int *)malloc(10*sizeof(int));
    	printf("the message is %s
    ",argc);
    	printf("the child id is %u
    ",pthread_self());
    	memset(p,'c',10);
    	printf("p=%x
    ",p);
    	pthread_exit(p);
    	//return 0;
    }

    结果例如以下:

    线程退出前操作

    线程在退出前也能够显示定义某些函数.可是不论是线程的正常退出还是异常终止,都存在资源释放的问题.在不考虑因执行出错而退出的前提下,怎样保证线程终止时,能顺利释放掉自己占有的资源,特别是锁资源,是个必须解决的问题.

    pthread_cleanup_push() & pthread_cleanup_pop()函数用于自己主动释放资源.採用先入后出的栈结构管理.

    详细能够參考这篇博文:http://blog.csdn.net/yanook/article/details/6579955以及源代码的函数使用说明

    以下的演示样例程序使用这两个函数,子线程运行死循环,在主线程中使用了cancel函数取消线程.

    #include<pthread.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<stdio.h>
    
    void cleanup()                       // 入栈的操作函数
    {
    	printf("cleanup
    ");
    }
    void *test_cancel(void)
    {
    	pthread_cleanup_push(cleanup,NULL);     // 函数放栈
    	printf("test_cancel
    ");            // 提示信息
    	while(1)                                // 死循环
    	{
    		printf("test message
    ");
    		sleep(1);
    	}
    	pthread_cleanup_pop(1);         // 出栈,并运行
    }
    int main()
    {
    	pthread_t tid;
    	pthread_create(&tid,NULL,(void *)test_cancel,NULL); // 创建线程
    	sleep(2);
    	pthread_cancel(tid);                                // 取消子线程
    	pthread_join(tid,NULL);
    }

    结果:

    取消线程

    1.取消线程是指取消一个正在运行的线程的操作,一个线程可以被取消并终止运行须要满足:

    (1)线程能否够被取消

    (2)线程处于取消点才干够取消,即使该线程被设置为能够取消状态,还有一个线程发起取消操作,该线程也不一定终止.

    函数pthread_cancel()用来向某线程发送取消操作,

    int pthread_cancel(pthread_t thread)
    发送终止信号给thread线程。假设成功则返回0。否则为非0值。发送成功并不意味着thread会终止。

    int pthread_setcancelstate(int state,   int *oldstate)  
    设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE
    分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续执行;old_state假设不为NULL则存入原来的Cancel状态以便恢复。

      

    int pthread_setcanceltype(int type, int *oldtype)  
    设置本线程取消动作的运行时机,type由两种取值:PTHREAD_CANCEL_DEFFEREDPTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和马上运行取消动作(退出)。oldtype假设不为NULL则存入运来的取消动作类型值。  

    void pthread_testcancel(void)
    是说pthread_testcancel在不包括取消点。可是又须要取消点的地方创建一个取消点。以便在一个没有包括取消点的运行代码线程中响应取消请求.
    线程取消功能处于启用状态且取消状态设置为延迟状态时,pthread_testcancel()函数有效。


    假设在取消功能处处于禁用状态下调用pthread_testcancel()。则该函数不起作用。
    请务必仅在线程取消线程操作安全的序列中插入pthread_testcancel()。除通过pthread_testcancel()调用以编程方式建立的取消点意外,pthread标准还指定了几个取消点。測试退出点,就是測试cancel信号.

    取消点

    线程取消的方法是向目标线程发Cancel信号,但怎样处理Cancel信号则由目标线程自己决定,或者忽略、或者马上终止、或者继续执行至Cancelation-point(取消点),由不同的Cancelation状态决定

    线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续执行至取消点。也就是说设置一个CANCELED状态。线程继续执行,仅仅有执行至Cancelation-point的时候才会退出。

    pthreads标准指定了几个取消点,当中包含:
    (1)通过pthread_testcancel调用以编程方式建立线程取消点。
    (2)线程等待pthread_cond_wait或pthread_cond_timewait()中的特定条件。


    (3)被sigwait(2)堵塞的函数
    (4)一些标准的库调用。通常。这些调用包含线程可基于堵塞的函数。
     
    缺省情况下,将启用取消功能。有时,您可能希望应用程序禁用取消功能。假设禁用取消功能,则会导致延迟全部的取消请求,
    直到再次启用取消请求。

     
    依据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及
    read()、write()等会引起堵塞的系统调用都是Cancelation-point,而其它pthread函数都不会引起Cancelation动作。


    可是pthread_cancel的手冊页声称,因为LinuxThread库与C库结合得不好。因而眼下C库函数都不是Cancelation-point;但CANCEL信号会使线程从堵塞的系统调用中退出。并置EINTR错误码,因此能够在须要作为Cancelation-point的系统调用前后调用pthread_testcancel()。从而达到POSIX标准所要求的目标.
    即例如以下代码段:
    pthread_testcancel();
    retcode = read(fd, buffer, length);
    pthread_testcancel();

    注意:
    程序设计方面的考虑,假设线程处于无限循环中,且循环体内没有运行至取消点的必定路径,则线程无法由外部其它线程的取消请求而终止。因此在这种循环体的必经路径上应该增加pthread_testcancel()调用.

    取消类型(Cancellation Type)

    我们会发现。通常的说法:某某函数是 Cancellation Points。这样的方法是easy令人混淆的。
    由于函数的运行是一个时间过程,而不是一个时间点。

    事实上真正的 Cancellation Points 仅仅是在这些函数中 Cancellation Type 被改动为 PHREAD_CANCEL_ASYNCHRONOUS 和改动回 PTHREAD_CANCEL_DEFERRED 中间的一段时间。

    POSIX的取消类型有两种,一种是延迟取消(PTHREAD_CANCEL_DEFERRED),这是系统默认的取消类型,即在线程到达取消点之前,不会出现真正的取消;第二种是异步取消(PHREAD_CANCEL_ASYNCHRONOUS)。使用异步取消时。线程能够在随意时间取消。

    线程终止的清理工作

    Posix的线程终止有两种情况:正常终止和非正常终止。
    线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出。这是可预见的退出方式。
    非正常终止是线程在其它线程的干预下,或者因为自身执行出错(比方訪问非法地址)而退出,这样的退出方式是不可预见的。

    不论是可预见的线程终止还是异常终止。都会存在资源释放的问题。在不考虑因执行出错而退出的前提下,怎样保证线程终止时能顺利的释放掉自己所占用的资源。特别是锁资源,就是一个必须考虑解决的问题。


    最常常出现的情形是资源独占锁的使用:线程为了訪问临界资源而为其加上锁,但在訪问过程中被外界取消,假设线程处于响应取消状态。且採用异步方式响应。或者在打开独占锁曾经的执行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确须要一个机制来简化用于资源释放的编程。

    在POSIX线程API中提供了一个pthread_cleanup_push()/ pthread_cleanup_pop()函数,
    对用于自己主动释放资源—从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包含调用pthread_exit()和取消点终止)都将运行pthread_cleanup_push()所指定的清理函数。

    API定义例如以下:
    void pthread_cleanup_push(void (*routine) (void *), void *arg)
    void pthread_cleanup_pop(int execute)

    pthread_cleanup_push()/pthread_cleanup_pop()採用先入后出的栈结构管理,void routine(void *arg)函数
    在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push() 的调用将在清理函数栈中形成一个函数链;
    从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包含调用pthread_exit()和异常终止,不包含return)
    都将运行pthread_cleanup_push()所指定的清理函数。

    在运行该函数链时依照压栈的相反顺序弹出。

    execute參数表示运行到 pthread_cleanup_pop()时
    是否在弹出清理函数的同一时候运行该函数,为0表示不运行,非0为运行;这个參数并不影响异常终止时清理函数的运行。

    pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的。这是pthread.h中的宏定义:

    #define pthread_cleanup_push(routine,arg)  
    { 
    struct _pthread_cleanup_buffer _buffer;  
    _pthread_cleanup_push (&_buffer, (routine), (arg));
    
    #define pthread_cleanup_pop(execute)  
    _pthread_cleanup_pop (&_buffer, (execute)); 
    }

    可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现。且必须位于程序的同一级别的代码段中才干通过编译。

    在以下的样例里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut)。以完毕解锁动作。

    pthread_cleanup_push(pthread_mutex_unlock, (void*) &mut);
    pthread_mutex_lock(&mut);
    /* do some work */
    pthread_mutex_unlock(&mut);
    pthread_cleanup_pop(0);
    或者
    void cleanup(void *arg)
    {    
        pthread_mutex_unlock(&mutex);
    }
    
    void* thread0(void* arg)
    {    
        pthread_cleanup_push(cleanup, NULL); // thread cleanup handler    p
        thread_mutex_lock(&mutex);    
        pthread_cond_wait(&cond, &mutex);    
        pthread_mutex_unlock(&mutex);    
        pthread_cleanup_pop(0);    
        pthread_exit(NULL);
    }

    取消线程演示样例

    以下的程序中,主线程使用cancel函数取消函数,因为子线程首先调用pthread_setcancelstate函数设置了线程的取消状态为PTHREAD_CANCEL_DISABLE,因此不可取消子线程,主线程处于状态.经过一段时间后,子线程调用pthread_setcancelstate函数设置了线程的取消状态是PTHREAD_CANCEL_ENABLE, 同意取消进程,从而使主线程可以取消子线程.

    代码例如以下:

    /*************************************************************************
    > File Name: pthread_cancle_exp.c
    > Author:SuooL 
    > Mail:1020935219@qq.com || hu1020935219@gmail.com
    > Website:http://blog.csdn.net/suool | http://suool.net
    > Created Time: 2014年08月13日 星期三 22时07分13秒
    > Description: 线程取消操作
    ************************************************************************/
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <pthread.h>
    
    void *thread_function(void *arg);
    
    int main(int argc,char *argv[]) 
    {
        int res;
        pthread_t a_thread;
        void *thread_result;
    
        res = pthread_create(&a_thread, NULL, thread_function, NULL); // 创建线程
        if (res != 0) 
        {
            perror("Thread creation failed");
            exit(EXIT_FAILURE);
        }
        printf("Cancelling thread...
    ");
        sleep(10);	
        res = pthread_cancel(a_thread);         // 取消子线程
        if (res != 0) 
        {
            perror("Thread cancelation failed");
            exit(EXIT_FAILURE);
        }
        printf("Waiting for thread to finish...
    ");
        sleep(10);	
        res = pthread_join(a_thread, &thread_result);   // 等待子线程结束
        if (res != 0) 
        {
            perror("Thread join failed");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    }
    
    void *thread_function(void *arg)      // 新线程运行函数
    {
        int i, res, j;
        sleep(1);
        res = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);  // 设置取消状态
        if (res != 0) 
        {
            perror("Thread pthread_setcancelstate failed");
            exit(EXIT_FAILURE);
        }
        printf("thread cancle type is disable,can't cancle this thread
    "); // 打印不可取消状态信息
        for(i = 0; i <3; i++) 
        {
            printf("Thread is running (%d)...
    ", i);
            sleep(1);
        }
    
        res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); // 设置取消状态
        if (res != 0) 
        {
            perror("Thread pthread_setcancelstate failed");
            exit(EXIT_FAILURE);
        }
        else
        printf("Now change ths canclestate is ENABLE
    ");
        sleep(20);        // 休眠20s
        pthread_exit(0);
    }
    

    线程与私有数据

    多线程程序中,全局变量是线程共同拥有的,但有时应用程序设计中有必要提供线程私有数据的全局变量.最简单的方式就是使用同名而不同地址的线程私有数据结构.这种数据结构由POSIX线程库维护,成为线程私有数据TSD.

    1.创建注销线程私有数据

    pthread_key_create()用来创建线程私有数据.

    注销一个TSD的  API是pthread_key_delete

    1.读写线程私有数据

    使用专门的POSIX THREAD函数,各自是

    pthread_setspecific和pthread_getspecific

    应用例如以下:

    #include<stdio.h>
    #include<pthread.h>
    #include<unistd.h>
    #include<stdlib.h>
    
    int key=100;          // 全局变量
    void *helloworld_one(char *argc)
    {
    	printf("the message is %s
    ",argc);
    	key=10;            // 改动为10
    	printf("key=%d,the child id is %u
    ",key,pthread_self()); // 打印id和key值
    	return 0;
    }
    
    void *helloworld_two(char *argc)
    {
    	printf("the message is %s
    ",argc);
    	sleep(1); // 等待前一个进程运行改动操作
    	printf("key=%d,the child id is %u
    ",key,pthread_self());  //打印....
    	return 0;
    }
    int main()
    {
    
    	pthread_t thread_id_one;
    	pthread_t thread_id_two;
    
    	pthread_create(&thread_id_one,NULL,(void *)*helloworld_one,"helloworld");  // 创建线程
    	pthread_create(&thread_id_two,NULL,(void *)*helloworld_two,"helloworld");  // 创建线程
    	pthread_join(thread_id_one,NULL);
    	pthread_join(thread_id_two,NULL); // 等待子线程结束
    }

    由此能够看出,一个进程改动全局变量也将影响另外进程的訪问.

    使用私有数据成员的演示样例:

    //this is the test code for pthread_key 
    
    #include <stdio.h> 
    #include <pthread.h> 
    
    pthread_key_t key;            // 线程私有数据
    
    void echomsg(void *t) 
    {                      // 退出时运行
    	printf("destructor excuted in thread %u,param=%u
    ",pthread_self(),((int *)t));  
    } 
    
    void * child1(void *arg) 
    { 
    	int i=10;
    	int tid=pthread_self(); 
    	printf("
    set key value %d in thread %u
    ",i,tid); 
    	pthread_setspecific(key,&i);         // 改动值为10 
    	printf("thread one sleep 2 until thread two finish
    "); // 线程2等待线程1改动完毕
    	sleep(2); 
    	printf("
    thread %u returns %d,add is %u
    ",tid,*((int *)pthread_getspecific(key)),(int *)pthread_getspecific(key));     // 打印当前线程值
    } 
    
    void * child2(void *arg) 
    { 
    	int temp=20;
    	int tid=pthread_self(); 
    	printf("
    set key value %d in thread %u
    ",temp,tid); 
    	pthread_setspecific(key,&temp);  // 改动值20
    	sleep(1); 
    	printf("thread %u returns %d,add is %u
    ",tid,*((int *)pthread_getspecific(key)),(int *)pthread_getspecific(key));       // 打印进程值
    } 
    
    int main(void) 
    { 
    	pthread_t tid1,tid2; 
    	pthread_key_create(&key,echomsg); 
    	pthread_create(&tid1,NULL,(void *)child1,NULL); 
    	pthread_create(&tid2,NULL,(void *)child2,NULL); 
    	pthread_join(tid1,NULL);
    	pthread_join(tid2,NULL);    // 等待进程结束
    	pthread_key_delete(key); 
    	return 0; 
    } 

    可见操作互不干扰.

    转载请注明出处,http://blog.csdn.net/suool/article/details/38542543,谢谢.

  • 相关阅读:
    Mysql 存储引擎中InnoDB与Myisam的主要区别
    [转]memmove函数
    _Obj* __STL_VOLATILE* __my_free_list
    [转]STL的内存分配器
    [转载]C++ 堆与栈简单的介绍
    [转载]__type_traits
    [转载]C++中 引用&与取地址&的区别
    [转载]delete指针之后应该赋值NULL
    [转载]C++中声明与定义的区别
    学习笔记ubuntu/shell
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7389771.html
Copyright © 2020-2023  润新知