2015-12-30 21:14:58 qq_21792169 阅读数 18104
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5133 人正在学习 去看看 朱有鹏

Linux下多线程详解pdf文档下载:点击这里!

Linux中线程和进程的区别http://blog.csdn.net/qq_21792169/article/details/50437304


线程退出的条件:下面任意一个都可以。

1.调用pthread_exit函数退出。

2.其他线程调用pthread_cancel取消该线程,且该线程可被取消。

3.创建线程的进程退出或者整个函数结束。

4.当前线程代码执行完毕。

5.其中的一个线程执行exec类函数执行新的代码,替换当前进程所有地址空间。


当线程中休眠或者死循环时候,需要在住进程中调用pthread_join等待线程结束,死循环可以通过另外一个休眠的线程来结束,举例说明,让LCD显示摄像头数据,但是我们中途需要点击触摸屏来退出显示,视频显示是一个死循环来不停的读取视频数据,那么我们就可以创建两个线程,一个负责视频的不停读取,一个负责获取触摸屏数据,没有数据就休眠,当休眠被唤醒后就调用pthread_cancel取消死循环的线程,设计思路基本是这样。也可以采取进程实现这个操作。


线程与进程
为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。

  使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。

  使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

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

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

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

3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

 

一、线程标识

  • 线程有ID, 但不是系统唯一, 而是进程环境中唯一有效.
  • 线程的句柄是pthread_t类型, 该类型不能作为整数处理, 而是一个结构.

下面介绍两个函数:

  • 头文件: <pthread.h>
  • 原型: int pthread_equal(pthread_t tid1, pthread_t tid2);
  • 返回值: 相等返回非0, 不相等返回0.
  • 说明: 比较两个线程ID是否相等.

 

  • 头文件: <pthread.h>
  • 原型: pthread_t pthread_self();
  • 返回值: 返回调用线程的线程ID.

二、线程创建

 在执行中创建一个线程, 可以为该线程分配它需要做的工作(线程执行函数), 该线程共享进程的资源. 创建线程的函数pthread_create()

  • 头文件: <pthread.h>
  • 原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(start_rtn)(void), void *restrict arg);
  • 返回值: 成功则返回0, 否则返回错误编号.
  • 参数:
    • tidp: 指向新创建线程ID的变量, 作为函数的输出.
    • attr: 用于定制各种不同的线程属性, NULL为默认属性(见下).
    • start_rtn: 函数指针, 为线程开始执行的函数名.该函数可以返回一个void *类型的返回值,而这个返回值也可以是其他类型,并由 pthread_join()获取
    • arg: 函数的唯一无类型(void)指针参数, 如要传多个参数, 可以用结构封装.

linux下多线程程序的编译方法:

       因为pthread的库不是linux系统的库,所以在进行编译的时候要加上     -lpthread

       # gcc filename -lpthread  //默认情况下gcc使用c库,要使用额外的库要这样选择使用的库

 

例:thread_create.c

#include <stdio.h>

#include <pthread.h>  //包线程要包含

void *mythread1(void)

{

   int i;

   for(i=0;i<100;i++)

   {

      printf("this is the 1st pthread,created by zieckey.\n");

      sleep(1);

   }

void *mythread2(void)

{

    int i;

for(i=0;i<100;i++)

   {

      printf("this is the 2st pthread,created by zieckey.\n");

      sleep(1);

   }

}

int main()

{

    int ret=0;

    pthread_tid1,id2;

   ret=pthread_create(&id1,NULL,(void*)mythread1,NULL);

    if(ret)

    {

        printf("create pthread error!\n");

         return -1; 

    }

   ret=pthread_create(&id2,NULL,(void*)mythread2,NULL);

    if(ret)

    {

        printf("create pthread error!\n");

         return  -1; 

    }

   pthread_join(id1,NULL);

   pthread_join(id2,NULL);

 

    return 0;

}

编译步骤:gcc thread_create .c -lpthread -othread_create

例2: thread_int.c  //向线程函数传递整形参数

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

void *create(void *arg)

{

    int *num;

    num=(int *)arg;

   printf("create parameter is %d \n",*num);

    return (void *)0;

}

int main(int argc,char *argv[])

{

   pthread_t tidp;

    int error;

    int test=4;

    int*attr=&test;

 

  error=pthread_create(&tidp,NULL,create,(void*)attr);

    if(error)

     {

       printf("pthread_create is created is not created...\n");

       return -1;

    }

   sleep(1);

  printf("pthread_create is created...\n");

   return 0;

}

注:字符串,结构参数,一样道理


三、线程属性

 pthread_create()中的attr参数是一个结构指针,结构中的元素分别对应着新线程的运行属性,主要包括以下几项:

 __detachstate,表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到  PTHREAD_CREATE_JOINABLE状态。

 

__schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和  SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过  pthread_setschedparam()来改变。

 

__schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。

 

__inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。

 

 __scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:  PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

 

pthread_attr_t结构中还有一些值,为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、 pthread_attr_destroy()和与各个属性相关的pthread_attr_get(),pthread_attr_set()函数。

 

  pthread_create()中,第二个参数(pthread_attr_t)为将要创建的thread属性。通常情况下配置为NULL,使用缺省设置就可以了。但了解这些属性,有利于更好的理解thread.

属性对象(pthread_attr_t)是不透明的,而且不能通过赋值直接进行修改。系统提供了一组函数,用于初始化、配置和销毁每种对象类型。

 创建属性:

int pthread_attr_init(pthread_attr_t *attr);

创建的属性设定为缺省设置。

 销毁属性:

int pthread_attr_destroy(pthread_attr_t *attr);

 

一:设置分离状态:

线程的分离状态有2种:PTHREAD_CREATE_JOINABLE(非分离状态), PTHREAD_CREATE_DETACHED(分离状态)

分离状态含义如下:

如果使用 PTHREAD_CREATE_JOINABLE 创建非分离线程,则假设应用程序将等待线程完成。也就是说,程序将对线程执行 pthread_join。 非分离线程在终止后,必须要有一个线程用 join 来等待它。否则,不会释放该线程的资源以供新线程使用,而这通常会导致内存泄漏。因此,如果不希望线程被等待,请将该线程作为分离线程来创建。

 

如果使用 PTHREAD_CREATE_DETACHED 创建分离thread,则表明此thread在退出时会自动回收资源和thread ID.

 

Sam之前很喜欢使用分离thread. 但现在慢慢使用中觉得这样是个不好的习惯。因为分离thread有个问题:主程序退出时,很难确认子thread已经退出。只好使用全局变量来标明子thread已经正常退出了。

另外:不管创建分离还是非分离的thread.在子thread全部退出之前退出主程序都是很有风险的。如果主thread选择return,或者调用exit()退出,则所有thread都会被kill掉。这样很容易出错。Sam上次出的问题其实就是这个。但如果主thread只是调用pthread_exit().则仅主线程本身终止。进程及进程内的其他线程将继续存在。所有线程都已终止时,进程也将终止。

 

intpthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

得到当前和分离状态和设置当前的分离状态。

 

二:设置栈溢出保护区大小:

栈溢出概念:

·                     溢出保护可能会导致系统资源浪费。如果应用程序创建大量线程,并且已知这些线程永远不会溢出其栈,则可以关闭溢出保护区。通过关闭溢出保护区,可以节省系统资源。

·                     线程在栈上分配大型数据结构时,可能需要较大的溢出保护区来检测栈溢出。

int pthread_attr_getguardsize(const pthread_attr_t *restrictattr,size_t *restrict guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guardsize);

设置和得到栈溢出保护区。如果guardsize设为0。则表示不设置栈溢出保护区。guardsize 的值向上舍入为PAGESIZE 的倍数。

 

三:设置thread竞用范围:

竞用范围(PTHREAD_SCOPE_SYSTEM 或 PTHREAD_SCOPE_PROCESS)指 使用 PTHREAD_SCOPE_SYSTEM 时,此线程将与系统中的所有线程进行竞争。使用 PTHREAD_SCOPE_PROCESS 时,此线程将与进程中的其他线程进行竞争。

 

int pthread_attr_getscope(const pthread_attr_t *restrict attr,int*restrict contentionscope);
int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);

 

四:设置线程并行级别:

int pthread_getconcurrency(void);
int pthread_setconcurrency(int new_level);

Sam不理解这个意思。

 

五:设置调度策略:

POSIX 标准指定 SCHED_FIFO(先入先出)、SCHED_RR(循环)或 SCHED_OTHER(实现定义的方法)的调度策略属性。

·                     SCHED_FIFO

如果调用进程具有有效的用户 ID 0,则争用范围为系统 (PTHREAD_SCOPE_SYSTEM) 的先入先出线程属于实时 (RT) 调度类。如果这些线程未被优先级更高的线程抢占,则会继续处理该线程,直到该线程放弃或阻塞为止。对于具有进程争用范围 (PTHREAD_SCOPE_PROCESS)) 的线程或其调用进程没有有效用户 ID 0 的线程,请使用 SCHED_FIFOSCHED_FIFO 基于 TS 调度类。

·                     SCHED_RR

如果调用进程具有有效的用户 ID 0,则争用范围为系统 (PTHREAD_SCOPE_SYSTEM)) 的循环线程属于实时 (RT) 调度类。如果这些线程未被优先级更高的线程抢占,并且这些线程没有放弃或阻塞,则在系统确定的时间段内将一直执行这些线程。对于具有进程争用范围 (PTHREAD_SCOPE_PROCESS) 的线程,请使用SCHED_RR(基于 TS 调度类)。此外,这些线程的调用进程没有有效的用户ID 0

SCHED_FIFO 是基于队列的调度程序,对于每个优先级都会使用不同的队列。SCHED_RR 与 FIFO 相似,不同的是前者的每个线程都有一个执行时间配额。

 

int pthread_attr_getschedpolicy(const pthread_attr_t *restrictattr,int *restrict policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

 

六:设置优先级:

int pthread_attr_getschedparam(const pthread_attr_t *restrictattr,struct sched_param *restrict param);

int pthread_attr_setschedparam(pthread_attr_t *restrict attr,
              conststruct sched_param *restrict param);

比较复杂,Sam没去研究。

 

七:设置栈大小:

当创建一个thread时,会给它分配一个栈空间,线程栈是从页边界开始的。任何指定的大小都被向上舍入到下一个页边界。不具备访问权限的页将被附加到栈的溢出端(第二项设置中设置)。

指定栈时,还应使用 PTHREAD_CREATE_JOINABLE 创建线程。在该线程的 pthread_join() 调用返回之前,不会释放该栈。在该线程终止之前,不会释放该线程的栈。了解这类线程是否已终止的唯一可靠方式是使用pthread_join

一般情况下,不需要为线程分配栈空间。系统会为每个线程的栈分配指定大小的虚拟内存。

#ulimit -a可以看到这个缺省大小

 四、线程终止

 

如果进程中的任一线程调用了exit,_Exit或者_exit,那么整个进程就会终止。与此类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。

单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止它的控制流。

(1):从启动例程中返回,返回值是线程的退出码

(2):线程可以被同一进程中的其他线程取消

(3):线程调用pthread_exit()

pthread_exit函数:

  • 原型: void pthread_exit(void *rval_ptr);
  • 头文件: <pthread.h>
  • 参数: rval_ptr是一个无类型指针, 指向线程的返回值存储变量.

 pthread_join函数:

  • 原型: int pthread_join(pthread_t thread, void **rval_ptr);
  • 头文件: <pthread.h>
  • 返回值: 成功则返回0, 否则返回错误编号.
  • 参数:
    • thread: 线程ID.
    • rval_ptr: 指向返回值的指针(返回值也是个指针).
  • 说明:
    • 调用线程将一直阻塞, 直到指定的线程调用pthread_exit, 从启动例程返回或被取消.
    • 如果线程从它的启动例程返回, rval_ptr包含返回码.
    • 如果线程被取消, 由rval_ptr指定的内存单元置为: PTHREAD_CANCELED.
    • 如果对返回值不关心, 可把rval_ptr设为NULL.

实例:

#include <pthread.h>

#include <stdio.h>

 

/* print process and thread IDs */

void printids(const char *s)

{

   pid_t pid, ppid;

   pthread_t tid;

     pid= getpid();

   ppid = getppid();

   tid = pthread_self();

    printf("%16s pid %5u ppid %5u tid %16u (0x%x) ",

            s, (unsigned int)pid, (unsigned int)ppid,

            (unsigned int)tid, (unsigned int)tid);

}

 /* thread process */

void *thread_func(void *arg);

{

   printids("new thread: ");

   return (void *)108;

}

 /* main func */

int main()

{

   int err;

   void *tret; /* thread return value */

   pthread_t ntid;

   err = pthread_create(&ntid, NULL, thread_func, NULL);

   if (err != 0)

       perror("can't create thread");

 

   err = pthread_join(ntid, &tret);

   if (err != 0)

       perror("can't join thread");

    printids("main thread: ");

   printf("thread exit code: %d ", (int)tret);

   sleep(1);

   return 0;

}

这段代码是通过前一个实例改编的执行流程如下:

  • 首先创建一个新线程, 该线程在打印完IDs后, 返回108.
  • 然后用pthread_join获取该线程返回值, 存于tret中.
  • 主线程打印IDs.
  • 主线程打印tret, 即新线程的返回值.

 

线程取消的定义:

一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。

 

线程取消的语义:

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

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

 

取消点定义:

根据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信号会使线程(http://blog.csdn.net/shanzhizi)从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用  pthread_testcancel(),从而达到POSIX标准所要求的目标,即如下代码段:

pthread_testcancel();

 

   retcode = read(fd, buffer, length);

 

   pthread_testcancel(); 

 

程序设计方面的考虑:

如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用。即如下代码段:

While(1)

{

    ………

    pthread_testcancel();

}

 

与线程取消相关的pthread函数:

intpthread_cancel(pthread_t thread):线程可以通过调用pthread_cancel函数来请求取消同一进程中的其他进程。

发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。注意pthread_cancel并不等待线程终止,它仅仅提出请求。

 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_DEFFERED和  PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入原来的取消动作类型值。

 

void pthread_testcancel(void)

检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。

 http://blog.csdn.net/shanzhizi

线程可以安排它退出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的。线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说它们的执行顺序与它们注册时的顺序相反。

#include <pthread.h>

void pthread_cleanup_push(void(*rtn)(void*),void *arg);

void pthread_cleanup_pop(int execute);

当线程执行以下动作时调用清理函数,调用参数为arg,清理函数的调用顺序用pthread_cleanup_push来安排。

调用pthread_exit

响应取消请求时

用非0的execute参数调用pthread_cleanup_pop时。

如果线程是通过从它的启动例程中返回而终止的话,那么它的清理处理程序就不会被调用,还要注意清理处理程序是按照与它们安装时相反的顺序被调用的。

 

int pthread_detach(pthread_t tid);

可以用于使线程进入分离状态。

 

2014-02-27 14:12:15 u012090276 阅读数 509
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5133 人正在学习 去看看 朱有鹏

读书笔记系列之:Linux多线程函数详解

                                                                                                   笛风

                                                                                               2013.12.9

原文网址为:http://blog.163.com/huchengsz@126/blog/static/734837452009981171140/

函数原型:                   

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr, void *(*start_rtn)(void),void *restrict arg);

    返回值:若是成功建立线程返回0,否则返回错误的编号
    形式参数:
               pthread_t *restrict tidp 要创建的线程的线程id指针
                constpthread_attr_t *restrict attr 创建线程时的线程属性
                void*(start_rtn)(void) 返回值是void类型的指针函数
                vodi*restrict arg   start_rtn的行参
   例题1:
       功能:测试建立一个新的线程
       程序名称:pthread_test.c      

#include <pthread.h>
#include <stdio.h>
void *create(void *arg)
...{
    printf("new thread created ..... ");
    
}
int main(int argc,char *argv[])
...{
    pthread_t tidp;
    int error;

    error=pthread_create(&tidp,NULL,create,NULL);
    if(error!=0)
    ...
...{
        printf("pthread_create is not created ... ");
        return -1;
    }
    printf("prthread_create is created... ");
    return 0;
}

    编译方法:

#gcc -Wall -lpthread pthread_test.c

   因为pthread的库不是linux系统的库,所以在进行编译的时候要加上-lpthread,否则编译不过,会出现下面错误

thread_test.c: 在函数 ‘create’ 中:
thread_test.c:7: 
警告: 在有返回值的函数中,程序流程到达函数尾
/tmp/ccOBJmuD.o: In function `main':thread_test.c:(.text+0x4f)
:对‘pthread_create’未定义的引用
collect2: ld 
返回 1

现在我们能建立了一个线程了,我们可以从函数的原型看到,在创建线程的时候,是可以在对我们的函数进行传递参数,在pthread_create的第四个行参。我们看一下例题2~3.
    例题2

    功能:向新的线程传递整形值
    程序名称:pthread_int.c   

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
...{
    int *num;
    num=(int *)arg;
    printf("create parameter is %d  ",*num);
    return (void *)0;
}
int main(int argc ,char *argv[])
...{
    pthread_t tidp;
    int error;
    
    int test=4;
    int *attr=&test;
    
    error=pthread_create(&tidp,NULL,create,(void *)attr);

    if(error!=0)
    
...{
        printf("pthread_create is created is not created ... ");
        return -1;
    }
    sleep(1);
    printf("pthread_create is created is  created ... ");
    return 0;
}

   编译方法:

gcc -lpthread thread_int.c -Wall

   执行结果:

create parameter is 4
pthread_create is created is  created ...

    例题总结:
    可以看出来,我们在main函数中传递的整行指针,传递到我们新建的线程函数中。
    在上面的例子可以看出来我们向新的线程传入了另一个线程的int数据,线程之间还可以传递字符串或是更复杂的数据结构。

    例题3:
    程序功能:向新建的线程传递字符串
    程序名称:thread_char.c   

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *create(void *arg)
...{
    char *name;
    name=(char *)arg;
    printf("arg is %s ",name);
    return (void *)0;
}
int main(int argc,char *argv[])
...{
    char *a="wang";
    int error;
    pthread_t tidp;

    error=pthread_create(&tidp,NULL,create,(void *)a);

    if(error!=0)
    
...{
        printf("pthread is not created ");
        return -1;
    }
    sleep(1);
    printf("pthread is created...");
    return 0;
}    


    编译方法:

gcc -Wall thread_char.c -lpthread


    执行结果:

arg is wang
pthread is created...


    例题总结:
    可以看出来main函数中的字符串传入了新建里的线程中。

    例题4
    程序名称:thread_struct.c   

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h> 
/**//*malloc()*/
struct test
...{
    int a;
    char *s;
};

void *create(void *arg)
...{
    struct test *temp;
    temp=(struct test *)arg;
    printf("test->a ==%d ",temp->a);
    printf("test->s ==%s ",temp->s);
    return (void *)0;
}
int main(int argc,char *argv[])
...{
    pthread_t tidp;
    int error;
    struct test *b;
    b=(struct test *)malloc(sizeof(struct test));
    b->a=4;
    b->s="wang";

    error=pthread_create(&tidp,NULL,create,(void *)b);

    if(error!=0)
    
...{
        printf("phread is not created...");
        return -1;
    }
    sleep(1);
    printf("pthread is created...");
    return 0;
}


    编译方法:

gcc -Wall -lpthread thread_struct.c


    执行结果:

test->a ==4
test->s ==wang
pthread is created...

线程包含了标识进程内执行环境必须的信息。他集成了进程中的所有信息都是对线程进行共享的,包括文本程序、程序的全局内存和堆内存、栈以及文件描述符。

    例题5
    程序目的:验证新建立的线程可以共享进程中的数据
    程序名称:thread_share.c   

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
static int a=4;
void *create(void *arg)
...{
    printf("new pthread ...");
    printf("a==%d ",a);
    return (void *)0;
}
int main(int argc,char *argv[])
...{
    pthread_t tidp;
    int error;
    a=5;

    error=pthread_create(&tidp,NULL,create,NULL);

    if(error!=0)
    
...{
        printf("new thread is not create ...");
        return -1;
    }
    sleep(1);
    printf("new thread is created ...");
    return 0;
}


    编译方法:

gcc -Wall -lpthread thread_share.c


    执行结果:

new pthread ...
a==5
new thread is created ...

    
    例题总结:可以看出来,我们在主线程更改了我们的全局变量a的值的时候,我们新建立的线程则打印出来了改变的值,可以看出可以访问线程所在进程中的数据信息。   

如果进程中任何一个线程中调用exit,_Exit,或者是_exit,那么整个进程就会终止,与此类似,如果信号的默认的动作是终止进程,那么,把该信号发送到线程会终止进程。
    线程的正常退出的方式:
       (1) 线程只是从启动例程中返回,返回值是线程中的退出码
       (2) 线程可以被另一个进程进行终止
       (3) 线程自己调用pthread_exit函数
    两个重要的函数原型:  

#include <pthread.h>
void pthread_exit(void *rval_ptr);
/*rval_ptr 
线程退出返回的指针*/

int pthread_join(pthread_t thread,void **rval_ptr);
   /*
成功结束进程为0,否则为错误编码*/

    例题6
    程序目的:线程正常退出,接受线程退出的返回码
    程序名称:exit_return.c   

   #include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
...{
    printf("new thread is created ...");
    return (void *)2;
}
int main(int argc,char *argv[])
...{
    pthread_t tid;
    int error;
    void *temp;

    error=pthread_create(&tid,NULL,create,NULL);

    if(error!=0)
    
...{
        printf("thread is not created ...");
        return -1;
    }
    error=pthread_join(tid,&temp);

    if(error!=0)
    
...{
        printf("thread is not exit ...");
        return -2;
    }
    printf("thread is exit code %d ",(int )temp);
    sleep(1);
    printf("thread is created...");
    return 0;
}


    编译方法:

gcc -Wall exit_return.c -lpthread


    执行结果:

new thread is created ...
thread is exit code 2
thread is created...


    线程退出不仅仅可以返回线程的int数值,还可以返回一个复杂的数据结构
    例题7
    程序目的:线程结束返回一个复杂的数据结构
    程序名称:exit_thread.c   

   #include <stdio.h>
#include <pthread.h>
#include <unistd.h>
struct test
...{
    int a;
    char *b;
};

struct test temp=
...{
    .a    =4,
    .b    ="wang"
};

void *create(void *arg)
...{
    printf("new thread ...");
    return (void *)&temp;
}

int main(int argc,char *argv[])
...{
    int error;
    pthread_t tid;
    struct test *c;

    error=pthread_create(&tid,NULL,create,NULL);
    
    if(error!=0)
    
...{
        printf("new thread is not created ...");
        return -1;
    }
    printf("main ...");

    error=pthread_join(tid,(void *)&c);

    if(error!=0)
    
...{
        printf("new thread is not exit ...");
        return -2;
    }
    printf("c->a ==%d ",c->a);
    printf("c->b ==%s ",c->b);
    sleep(1);
    return 0;
}


    编译方法:
 

gcc -Wall -lpthread exit_struct.c

    
    执行结果:

main ...
new thread ...
c->a ==4
c->b ==wang


    例题总结:一定要记得返回的数据结构要是在这个数据要返回的结构没有释放的时候应用,如果数据结构已经发生变化,那返回的就不会是我们所需要的,而是藏数据阿。

函数原型:
   

#include <pthread.h>
pthread_t pthread_self(void);


    例题8
    程序目的:实现在新建立的线程中打印该线程的id和进程id
    程序名称:thread_self.c
   

#include <stdio.h>
#include <pthread.h>
#include <unistd.h> /*getpid()*/
void *create(void *arg)
{
    printf("new thread .... ");
    printf("thread_tid==%u  ",(unsigned int)pthread_self());
    printf("thread pid is %d  ",getpid());
    return (void *)0;
}
int main(int argc,char *argv[])
{
    pthread_t tid;
    int error;

    printf("Main thread is starting ... ");

    error=pthread_create(&tid,NULL,create,NULL);

    if(error!=0)
    {
        printf("thread is not created ... ");
        return -1;
    }
    printf("main pid is %d  ",getpid());
    sleep(1);
    return 0;
}


    编译方法:

   

gcc -Wall -lpthread thread_self.c


    执行结果:

Main thread is starting ...
main pid is 6860
new thread ....
thread_tid==3084954544
thread pid is 6860


 

   4、 线程的处理程序

   函数原型:   

#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *),void *arg);
    函数rtn是清理函数,arg是调用参数

void pthread_cleanup_pop(int execute);

   在前面讲过线程的终止方式,是正常终止还是非正常终止,都会存在一个资源释放的问题,在posix中提供了一组,就是我们上面看的函数进行线程退出的处理 函数,有些像在进程中的atexit函数。释放的方式是指pthread_cleanup_push的调用点到pthread_cleanup_pop之 间程序段进行终止。

  pthread_cleanup_push()/pthread_cleanup_pop采用先入后出的方式的栈的管理方式,void*rtn(void *),在执行pthread_cleanup_push()时压入函数栈,多次执行pthread_cleanup_push()形成一个函数链,在执行 这个函数链的时候会以反方向弹出,即先入后出。execute参数表识,是否执行弹出清理函数,当execute=0时不进行弹出清理函数,非零的时候弹 出处理函数。

   例题9

   程序目的:实现在正常结束线程的时候,进行函数处理

   程序名称:thread_clean.c

   编译方法:

   执行结果: 

thread 1 start
thread 1 push complete
thread 1 exit code 1
thread 2 start
thread 2 push complete
cleanup :thread 2 second handler
cleanup :thread 2 first handler
thread 2 exit code 2


 

 gcc -Wall -lpthread thread_clean.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *clean(void *arg)
{
    printf("cleanup :%s  ",(char *)arg);
    return (void *)0;
}
void *thr_fn1(void *arg)
{
    printf("thread 1 start  ");
    pthread_cleanup_push(clean,"thread 1 first handler");
    pthread_cleanup_push(clean,"thread 1 second hadler");
    printf("thread 1 push complete  ");
    if(arg)
    {
        return((void *)1);
    }
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return (void *)1;
}
void *thr_fn2(void *arg)
{
    printf("thread 2 start  ");
    pthread_cleanup_push(clean,"thread 2 first handler");
    pthread_cleanup_push(clean,"thread 2 second handler");
    printf("thread 2 push complete  ");
    if(arg)
    {
        pthread_exit((void *)2);
    }
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    pthread_exit((void *)2);
}
int main(void)
{
    int err;
    pthread_t tid1,tid2;
    void *tret;

    err=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
    if(err!=0)
    {
        printf("error .... ");
        return -1;
    }
    err=pthread_create(&tid2,NULL,thr_fn2,(void *)1);

    if(err!=0)
    {
        printf("error .... ");
        return -1;
    }
    err=pthread_join(tid1,&tret);
    if(err!=0)
    {
        printf("error .... ");
        return -1;
    }
    printf("thread 1 exit code %d  ",(int)tret);

    err=pthread_join(tid2,&tret);
    if(err!=0)
    {
        printf("error .... ");
        return -1;
    }

 gcc -Wall -lpthread thread_clean.c


    printf("thread 2 exit code %d  ",(int)tret);
    exit(0);
}


2007-07-05 12:01:00 Frozen_fish 阅读数 3257
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5133 人正在学习 去看看 朱有鹏
 本文可任意转载,但必须注明作者和出处。
【原创】手把手教你Linux下的多线程设计(一)
                                      --Linux下多线程编程详解
 
原创作者:Frozen_socker(冰棍)  
 E_mail:dlskyfly@163.com       
 线程也被称为轻权进程(lightweight process)。
 
  在传统的UNIX上,一个进程让另一个实体做某个事务是用fork派生子进程的方法处理的。派生子进程的代价比线程要昂贵得多,尤其是在父子进程之间、子进程之间传递信息需要用IPC或其他方法通信。相对比,使用线程有许多优点,如创建线程要比创建进程快的多、一个进程中的线程共享相同的全局存储区等等。
 
   Linux系统下的多线程遵循POSIX线程接口,称为pthread,在Linux中,多线程需要使用的头文件为<pthread.h>,连接时需要使用库为libpthread.a。
 
 
 
我们编写一个非常简单的例子:
 
//example_1.c
#include <stdio.h>
#include 
<pthread.h>
 
void * pthread_func_test(void * arg);
 
int
 main()
{
     pthread_t pt1,pt2;
     pthread_create(
&pt1,NULL,pthread_func_test,"This is the Thread_ONE"
);
     pthread_create(
&pt2,NULL,pthread_func_test,"This is the Thread_TWO"
);
     sleep(
1);              //不加上这句,看不到结果。

}

void * pthread_func_test(void * arg)
{
     printf(
"%s /n "
,arg);
}
 
 
 
编译源文件:
gcc example_1.c -o example -lpthread
 
 
 
编译环境:
平   台:FC6
版   本:2.6.18-1.2798.fc6
编译器:gcc 4.1.1 20070105 (Red Hat 4.1.1-51)
 
 
 
 
运行可执行文件:
./example
 
 
 
在终端上的输出结果:
This is the Thread_ONE
This 
is the Thread_TWO
 
 
 
  在example_1这个例子中,一共产生了三个线程,第一个就是main所代表的主线程,另外两个就是pt1和pt2分别代表的两个分支线程,这两个线程由pthread_create函数创建,执行的内容就是写在函数pthread_func_test里的东东。
上例涉及到的函数是:pthread_create()
函数原型如下:
int pthread_create(pthread_t *restrict thread,
           
const pthread_attr_t *
restrict attr,
           
void *(*start_routine)(void*), void *restrict arg);
 
 
 
参数点解:
1、每个线程都有自己的ID即thread ID,可以简称tid,呵呵,是不是想起什么来了?。。。对,和pid有点象。其类型为pthread_t,pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
         typedef unsigned long int pthread_t;
         可以看成是线程的标志符。当成功创建一个新线程的时候,系统会为该线程分配一个tid,并将该值通过指针返回给调用它的程序。
2、attr申明线程的属性。                       
     属性结构为pthread_attr_t,它在头文件/usr/include/pthread.h中定义。设为NULL,表示在这里我们只使用线程的默认属性就可以了。
    
3、start_routine表示新创建的线程所要执行的例程。线程以调用该函数开始,直到由该函数返回(return)终止这个线程,或者在start_routine所指向的函数中调用pthread_exit函数终止。start_routine只有一个参数,该参数由随后的arg指针来指出。
 
4、arg:也是一个指针,也就是start_routine指针所指向的函数的参数。
 
 
 
返回值:

  当pthread_create调用成功时,该调用返回0;否则,返回一个错误代码指出错误的类型。 

 

       欢迎您发邮件与我交流,但因为工作和时间的关系,我有权对您提出的一些问题不予回答,敬请见谅。

2007-07-09 14:55:00 Frozen_fish 阅读数 3626
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5133 人正在学习 去看看 朱有鹏

本文可任意转载,但必须注明作者和出处。

【原创】手把手教你Linux下的多线程设计(三)
                                      --Linux下多线程编程详解
 
原创作者:Frozen_socker(冰棍)  
 E_mail:dlskyfly@163.com

线程互斥

 

互斥操作,就是对某段代码或某个变量修改的时候只能有一个线程在执行这段代码,其他线程不能同时进入这段代码或同时修改该变量。这个代码或变量称为临界资源。

 

 

例如:有两个线程A和B,临界资源为X,首先线程A进入,将X置为加锁状态,在A将锁打开之前的这段时间里,如果此时恰巧线程B也欲获得X,但它发现X处于加锁状态,说明有其它线程正在执行互斥部分,于是,线程B将自身阻塞。。。线程A处理完毕,在退出前,将X解锁,并将其它线程唤醒,于是线程B开始对X进行加锁操作了。通过这种方式,实现了两个不同线程的交替操作。

 

 

 记住一个互斥体永远不可能同时属于两个线程。或者处于锁定状态;或者空闲中,不属于任何一个线程。

 

 

 

 代码如下:

//example_3.c
#include <stdio.h>
#include 
<pthread.h>

void * pthread_func_test(void * arg);

pthread_mutex_t mu;

int
 main()
{
    
int
 i;
    pthread_t pt;
    
    pthread_mutex_init(
&mu,NULL);        //声明mu使用默认属性,此行可以不写

    pthread_create(&pt,NULL,pthread_func_test,NULL);
    
for(i = 0; i < 3; i++
)
    
{
        pthread_mutex_lock(
&
mu);
        printf(
"主线程ID是:%lu  ",pthread_self());        //pthread_self函数作用:获得当前线程的id

        pthread_mutex_unlock(&mu);
        sleep(
1
);
    }
    
}


void * pthread_func_test(void * arg)
{
    
int
 j;
    
for(j = 0; j < 3; j++
)
    
{
        pthread_mutex_lock(
&
mu);
        printf(
"新线程ID是:%lu  "
,pthread_self());
        pthread_mutex_unlock(
&
mu);
        sleep(
1
);
    }

}

 

 

 

终端输出结果:

主线程ID是 : 3086493376
新线程ID是 : 
3086490512

主线程ID是 : 
3086493376
新线程ID是 : 
3086490512

主线程ID是 : 
3086493376
新线程ID是 : 
3086490512

 

注:在你机器上运行的结果很可能与这里显示的不一样。

 

 

   

pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,都处于加锁状态中,即同一时间只能被一个线程调用执行。当另一个线程执行到pthread_mutex_lock处时,如果该锁此时被其它线程使用,那么该线程被阻塞,即程序将等待到其它线程释放此互斥锁。

 

 

 

上述例子中,涉及到了几个函数:pthread_mutex_init/pthread_mutex_lock/pthread_mutex_unlock/pthread_mutex_destroy/pthread_self

 

 

  函数原型:

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                                                
const pthread_mutexattr_t *restrict attr);

 

函数作用:

初始化互斥体类型变量mutex,变量的属性由attr进行指定。attr设为NULL,即采用默认属性,这种方式与pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER的方式等价。

 

 

函数原型: 

int pthread_mutex_lock(pthread_mutex_t *mutex);

 

函数作用:

用来锁住互斥体变量。如果参数mutex所指的互斥体已经被锁住了,那么发出调用的线程将被阻塞直到其他线程对mutex解锁为止。

 

 

 

函数原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

 

函数作用:

如果当前的线程拥有参数mutex所指定的互斥体,那么该函数调用将该互斥体解锁。

 

 

 

函数原型:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

 

函数作用:

用来释放互斥体所占用的资源。

 

 

 

函数原型:

pthread_t pthread_self(void);

函数作用:获得线程自身的ID。前面我们已经提到过,pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生奇怪的结果。

  

 

 

但是上面的代码并不完善,假设将循环次数修改得足够的长,打印后的结果可能并不是我们所希望看到的交替打印,可能象下面这样:

主线程ID是 : 3086493376
新线程ID是 : 
3086490512
 
主线程ID是 : 
3086493376
新线程ID是 : 
3086490512

新线程ID是 : 
3086490512
主线程ID是 : 
3086493376

 

 

 

这是什么原因呢?因为Linux是分时操作系统,采用的是时间片轮转的方式,主线程和新线程可能因为其它因素的干扰,获得了非顺序的时间片。如果想要严格的做到“交替”方式,可以略施小计,即加入一个标志。

 

 

   

完整程序如下:

//example_4.c
#include <stdio.h>
#include 
<pthread.h>

void * pthread_func_test(void * arg);

pthread_mutex_t mu;
int flag = 0
;

int
 main()
{
    
int
 i;
    pthread_t pt;
    
    pthread_mutex_init(
&
mu,NULL);
    pthread_create(
&
pt,NULL,pthread_func_test,NULL);
    
for(i = 0; i < 3; i++
)
    
{
        pthread_mutex_lock(
&
mu);
        
if(flag == 0
)
                printf(
"主线程ID是:%lu  "
,pthread_self());    
        flag 
= 1
;        
        pthread_mutex_unlock(
&
mu);
        sleep(
1
);
    }

    pthread_join(pt, NULL);
    pthread_mutex_destroy(
&mu);    
}


void * pthread_func_test(void * arg)
{
    
int
 j;
    
for(j = 0; j < 3; j++
)
    
{
        pthread_mutex_lock(
&
mu);
        
if(flag == 1
)
            printf(
"新线程ID是:%lu  "
,pthread_self());
        flag 
== 0
;
        pthread_mutex_unlock(
&
mu);
        sleep(
1
);
    }

}

 

 

在使用互斥锁的过程中很有可能会出现死锁:即两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁2,A线程先锁定互斥锁1,B线程先锁定互斥锁2,这时就出现了死锁。此时我们可以使用函数pthread_mutex_trylock,该函数企图锁住一个互斥体,但不阻塞。

 

 

 

函数原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

函数pthread_mutex_trylock()用来锁住参数mutex所指定的互斥体。如果参数mutex所指的互斥体已经被上锁,该调用不会阻塞等待互斥体的解锁,而会返回一个错误代码。通过对返回代码的判断,程序员就可以针对死锁做出相应的处理。所以在对多个互斥体编程中,尤其要注意这一点。

 

 

 

经过以上的讲解,我们就学习了Linux下关于多线程方面对互斥体变量的操作。下一节,将给大家讲解有关线程同步方面的知识点。

2016-08-13 01:48:57 daiyudong2020 阅读数 855
  • linux线程全解-linux应用编程和网络编程第7部分

    本课程讲解linux中线程,首先使用多进程解决上个课程中提出的并发式读取按键和鼠标的任务,然后引出多线程并讲解多线程的优势,后详细讲了多线程的同步技术。学习本课程的目的是学会在linux应用编程中使用多线程技术。

    5133 人正在学习 去看看 朱有鹏


注:如果unubtu下 man手册不全,需要安装 apt-get install manpages-posix-dev


一、线程创建

PTHREAD_CREATE(3)          Linux Programmer's Manual         PTHREAD_CREATE(3)

NAME
       pthread_create - create a new thread

SYNOPSIS
       #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.
返回值:若成功,返回0;否则,返回错误编码
参数:第二参数attr是线程的属性,可以设置为NULL使用默认属性


二、获取线程ID

PTHREAD_SELF(3)            Linux Programmer's Manual           PTHREAD_SELF(3)

NAME
       pthread_self - obtain ID of the calling thread

SYNOPSIS
       #include <pthread.h>

       pthread_t pthread_self(void);

       Compile and link with -pthread.
返回值:调用线程的线程ID


三、线程ID比较

PTHREAD_EQUAL(3)           Linux Programmer's Manual          PTHREAD_EQUAL(3)

NAME
       pthread_equal - compare thread IDs

SYNOPSIS
       #include <pthread.h>

       int pthread_equal(pthread_t t1, pthread_t t2);

       Compile and link with -pthread.
返回值:若相等,返回非0数值;否则,返回0


四、线程终止

PTHREAD_EXIT(3)            Linux Programmer's Manual           PTHREAD_EXIT(3)

NAME
       pthread_exit - terminate calling thread

SYNOPSIS
       #include <pthread.h>

       void pthread_exit(void *retval);

       Compile and link with -pthread.
retval参数是一个无类型的指针,进程中的其他线程也可以通过调用pthread_join访问到这个指针


五、阻塞等待指定线程退出

PTHREAD_JOIN(3)            Linux Programmer's Manual           PTHREAD_JOIN(3)

NAME
       pthread_join - join with a terminated thread

SYNOPSIS
       #include <pthread.h>

       int pthread_join(pthread_t thread, void **retval);

       Compile and link with -pthread.
返回值:若成功,返回0;否则,返回错误编码

注:如果线程已经处于分离状态,pthread_join就会处于调用失败。


六、取消同一进程中的其他线程

PTHREAD_CANCEL(3)          Linux Programmer's Manual         PTHREAD_CANCEL(3)

NAME
       pthread_cancel - send a cancellation request to a thread

SYNOPSIS
       #include <pthread.h>

       int pthread_cancel(pthread_t thread);

       Compile and link with -pthread.
返回值:若成功,返回0;否则,返回错误编码

注:线程可以选择忽略取消或者控制如何被取消。


七、分离线程函数

PTHREAD_DETACH(3)          Linux Programmer's Manual         PTHREAD_DETACH(3)

NAME
       pthread_detach - detach a thread

SYNOPSIS
       #include <pthread.h>

       int pthread_detach(pthread_t thread);

       Compile and link with -pthread.
返回值:若成功,返回0;否则,返回错误编码


一个比较完整的例子,用到了上面的7个函数:gcc pthread_create.c -pthread

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

void Perror(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}

void* fun2(void *arg)
{
    pthread_t thread_id = pthread_self();
    printf("the thread2 id is %ld, arg is %d\n", (long)thread_id, *(int*)arg);
    sleep(1); /* wait for join */
    pthread_exit((void*)2);
}

void* fun3(void *arg)
{
    pthread_t thread_id = pthread_self();
    printf("the thread3 id is %ld, arg is %d\n", (long)thread_id, *(int*)arg);
    sleep(60); /* wait for cancel */
}

int main()
{
    int err;
    pthread_t thread1;
    pthread_t thread2;
    pthread_t thread3;
    int arg2 = 2;
    int arg3 = 3;

    thread1 = pthread_self();
    printf("the thread1 id is %ld\n", (long)thread1);

    // Create thread
    err = pthread_create(&thread2, NULL, fun2, (void*)&arg2);
    if (err != 0) {
        Perror("can't create thread2\n");
    }
    err = pthread_create(&thread3, NULL, fun3, (void*)&arg3);
    if (err != 0) {
        Perror("can't create thread3\n");
    }

    // detach thread3
    err = pthread_detach(thread3);
    if (err != 0) {
        Perror("can't detach thread3\n");
    }

    // if equal
    if (pthread_equal(thread2, thread3) != 0)
        printf("the thread2 and thread3 same\n");
    else
        printf("the thread2 and thread3 diff\n");

    // wait thread2 exit
    void *retval;
    int res = pthread_join(thread2, &retval);
    if (res != 0)
        printf("can't join with thread2\n");
    else
        printf("thread2 exit code %ld\n", (long)retval);

    // cancel thread3
    err = pthread_cancel(thread3);
    if (err != 0) {
        Perror("can't cancel thread3\n");
    }

    sleep(60);
    return 0;
}

运行结果:


注:通过pstree命令也观察到线程数如程序设置一样。


参考:《unix环境高级编程》·第三版


End;


没有更多推荐了,返回首页