您当前的位置:五五电子网电子知识电子学习基础知识电脑-单片机-自动控制Linux下的多线程编程 正文
Linux下的多线程编程

Linux下的多线程编程

点击数:7472 次   录入时间:03-04 11:55:44   整理:http://www.55dianzi.com   电脑-单片机-自动控制

    #include

    pthread_attr_t attr;

    pthread_t tid;

    /*初始化属性值,均设为默认值*/

    pthread_attr_init(&attr);

    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

    pthread_create(&tid, &attr, (void *) my_function, NULL);

    线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。

    另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。

    #include

    #include

    pthread_attr_t attr;

    pthread_t tid;

    sched_param param;

    int newprio=20;

    pthread_attr_init(&attr);

    pthread_attr_getschedparam(&attr, ¶m);

    param.sched_priority=newprio;

    pthread_attr_setschedparam(&attr, ¶m);

    pthread_create(&tid, &attr, (void *)myfunction, myarg);

    4 线程的数据处理

    和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义,这是为了防止编译器在优化时(如gcc中使用-OX参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。下面,我们就逐步介绍处理线程数据时的有关知识。

    4.1 线程数据

    在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。

    和线程数据相关的函数主要有4个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。

    创建键的函数原型为:

    extern int pthread_key_create __P ((pthread_key_t *__key,

    void (*__destr_function) (void *)));

    第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用,为了让这个键只被创建一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。

    在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数createWindow,这个函数定义一个图形窗口(数据类型为Fl_Window *,这是图形界面开发工具FLTK中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。

    /* 声明一个键*/

    pthread_key_t myWinKey;

    /* 函数 createWindow */

    void createWindow ( void ) {

    Fl_Window * win;

    static pthread_once_t once= PTHREAD_ONCE_INIT;

    /* 调用函数createMyKey,创建键*/

    pthread_once ( & once, createMyKey) ;

    /*win指向一个新建立的窗口*/

    win=new Fl_Window( 0, 0, 100, 100, "MyWindow");

    /* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/

    setWindow(win);

    /* 将窗口指针值绑定在键myWinKey上*/

    pthread_setpecific ( myWinKey, win);

    }

    /* 函数 createMyKey,创建一个键,并指定了destructor */

    void createMyKey ( void ) {

    pthread_keycreate(&myWinKey, freeWinKey);

    }

    /* 函数 freeWinKey,释放空间*/

    void freeWinKey ( Fl_Window * win){

    delete win;

    }

    这样,在不同的线程中调用函数createMyWin,都可以得到在线程内部均可见的窗口变量,这个变量通过函数 pthread_getspecific得到。在上面的例子中,我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原型如下:

    extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));

    extern void *pthread_getspecific __P ((pthread_key_t __key));

    这两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。

    4.2 互斥锁

    互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见:假设各个线程向同一个文件顺序写入数据,最后得到的结果一定是灾难性的。

    我们先看下面一段代码。这是一个读/写程序,它们公用一个缓冲区,并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态:有信息或没有信息。

    void reader_function ( void );

    void writer_function ( void );

    char buffer;

    int buffer_has_item=0;

    pthread_mutex_t mutex;

    struct timespec delay;

    void main ( void ){

    pthread_t reader;

    /* 定义延迟时间*/

    delay.tv_sec = 2;

    delay.tv_nec = 0;

    /* 用默认属性初始化一个互斥锁对象*/

    pthread_mutex_init (&mutex,NULL);

    pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);

    writer_function( );

    }

    void writer_function (void){

    while(1){

    /* 锁定互斥锁*/

    pthread_mutex_LOCk (&mutex);

    if (buffer_has_item==0){

    buffer=make_new_item( );

上一页  [1] [2] [3] [4]  下一页


本文关键字:Linux  多线程  电脑-单片机-自动控制电子学习 - 基础知识 - 电脑-单片机-自动控制

上一篇:运用JDOM创建XML