pthread_join函数及linux线程

pthread_join函数及linux线程

分类: 程序相关   1045人阅读  评论(2)  收藏  举报
pthread_join使一个线程等待另一个线程结束。

代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。
下面说一下如何创建一个线程。
通过创建线程,线程将会执行一个线程函数,该线程格式必须按照下面来声明:
       void * Thread_Function(void *)
创建线程的函数如下:
       int pthread_create(pthread_t *restrict thread,
              const pthread_attr_t *restrict attr,
              void *(*start_routine)(void*), void *restrict arg);
下面说明一下各个参数的含义:
thread:所创建的线程号。
attr:所创建的线程属性,这个将在后面详细说明。
start_routine:即将运行的线程函数。
art:传递给线程函数的参数。
下面是一个简单的创建线程例子:
#include <pthread.h>
#include <stdio.h>
/* Prints x’s to stderr. The parameter is unused. Does not return. */
void* print_xs (void* unused)
{
while (1)
fputc (‘x’, stderr);
return NULL;
}
/* The main program. */
int main ()
{
pthread_t thread_id;
/* Create a new thread. The new thread will run the print_xs
function. */
pthread_create (&thread_id, NULL, &print_xs, NULL);
/* Print o’s continuously to stderr. */
while (1)
fputc (‘o’, stderr);
return 0;
}
在编译的时候需要注意,由于线程创建函数在libpthread.so库中,所以在编译命令中需要将该库导入。命令如下:
gcc –o createthread –lpthread createthread.c
如果想传递参数给线程函数,可以通过其参数arg,其类型是void *。如果你需要传递多个参数的话,可以考虑将这些参数组成一个结构体来传递。另外,由于类型是void *,所以你的参数不可以被提前释放掉。
下面一个问题和前面创建进程类似,不过带来的问题回避进程要严重得多。如果你的主线程,也就是main函数执行的那个线程,在你其他县城推出之前就已经退出,那么带来的bug则不可估量。通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出。
int pthread_join(pthread_t thread, void **value_ptr);
thread:等待退出线程的线程号。
value_ptr:退出线程的返回值。
下面一个例子结合上面的内容:
int main ()
{
pthread_t thread1_id;
pthread_t thread2_id;
struct char_print_parms thread1_args;
struct char_print_parms thread2_args;
/* Create a new thread to print 30,000 x’s. */
thread1_args.character = ’x’;
thread1_args.count = 30000;
pthread_create (&thread1_id, NULL, &char_print, &thread1_args);
/* Create a new thread to print 20,000 o’s. */
thread2_args.character = ’o’;
thread2_args.count = 20000;
pthread_create (&thread2_id, NULL, &char_print, &thread2_args);
/* Make sure the first thread has finished. */
pthread_join (thread1_id, NULL);
/* Make sure the second thread has finished. */
pthread_join (thread2_id, NULL);
/* Now we can safely return. */
return 0;
}
下面说一下前面提到的线程属性。
在我们前面提到,可以通过pthread_join()函数来使主线程阻塞等待其他线程退出,这样主线程可以清理其他线程的环境。但是还有一些线程,更喜欢自己来清理退出的状态,他们也不愿意主线程调用pthread_join来等待他们。我们将这一类线程的属性称为detached。如果我们在调用pthread_create()函数的时候将属性设置为NULL,则表明我们希望所创建的线程采用默认的属性,也就是jionable。如果需要将属性设置为detached,则参考下面的例子:
#include <stdio.h>
#include <pthread.h>
void * start_run(void * arg)
{
        //do some work
}
int main()
{
        pthread_t thread_id;
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
        pthread_create(&thread_id,&attr,start_run,NULL);
        pthread_attr_destroy(&attr);
        sleep(5);
        exit(0);
}
在线程设置为joinable后,可以调用pthread_detach()使之成为detached。但是相反的操作则不可以。还有,如果线程已经调用pthread_join()后,则再调用pthread_detach()则不会有任何效果。
线程可以通过自身执行结束来结束,也可以通过调用pthread_exit()来结束线程的执行。另外,线程甲可以被线程乙被动结束。这个通过调用pthread_cancel()来达到目的。
int pthread_cancel(pthread_t thread);
       函数调用成功返回0。
当然,线程也不是被动的被别人结束。它可以通过设置自身的属性来决定如何结束。
线程的被动结束分为两种,一种是异步终结,另外一种是同步终结。异步终结就是当其他线程调用pthread_cancel的时候,线程就立刻被结束。而同步终结则不会立刻终结,它会继续运行,直到到达下一个结束点(cancellation point)。当一个线程被按照默认的创建方式创建,那么它的属性是同步终结。
通过调用pthread_setcanceltype()来设置终结状态。
int pthread_setcanceltype(int type, int *oldtype);
state:要设置的状态,可以为PTHREAD_CANCEL_DEFERRED或者为PTHREAD_CANCEL_ASYNCHRONOUS。
那么前面提到的结束点又是如何设置了?最常用的创建终结点就是调用pthread_testcancel()的地方。该函数除了检查同步终结时的状态,其他什么也不做。
上面一个函数是用来设置终结状态的。还可以通过下面的函数来设置终结类型,即该线程可不可以被终结:
int pthread_setcancelstate(int state, int *oldstate);
       state:终结状态,可以为PTHREAD_CANCEL_DISABLE或者PTHREAD_CANCEL_ENABLE。具体什么含义大家可以通过单词意思即可明白。
最后说一下线程的本质。 其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。


pthread_create 参数传递指针问题
2010-11-04 15:52

linux 下常用的创建多线程函数pthread_create(pthread_t * thread , pthread_attr_t * attr , void *(*start_routine)(void*) , void *args);其中第一个参数用来保存线程信息,第二个参数指新线程的运行属性,可以设置为NULL,第三个参数为自定义的线程函数,第四个参数就是线程函数需要用到的参数,一般如果要传递多个参数,可以设置为结构体(struct)类型,这里我们使用int类型的变量。 下面我着重讨论一个用for结构来创建多个线程时参数传递的问题

先看下面的例子,后面附有结果,先不要看,猜测一下会有什么样的输出:

#include<iostream>
#include<pthread.h>
#include<semaphore.h>
using namespace std;

#define th_pop 20 //

pthread_mutex_t mutex;

pthread_t a_thread[th_pop];


void * thread_func(void *args)
{
    pthread_mutex_lock(&mutex);
    int t_id = *(int*)args;
    cout<<"the id of this thread is "<<t_id<<endl;
    pthread_mutex_unlock(&mutex);    
    return (void*)NULL;
}

void init()
{
    pthread_mutex_init(&mutex, NULL);
    for(int i=0; i<th_pop; i++)
    {
        pthread_create(&a_thread[i] , NULL , thread_func , &i);
    }
    //wait the end of the threads;
    for(int i=0; i<th_pop; i++)
    {
        int res = pthread_join(a_thread[i] , NULL);
        if(res != 0)
            cout<<"the thread id: "<< i<<" ends fail"<<endl;
    }
    pthread_mutex_destroy(&mutex);
    
}

int main()
{
    init();
    return 0;
}

编译运行

g++ -fpermissive args.cc -o args.o -pthread
./args.o

下面是输出结果,由于线程执行的不确定性,可能你执行的时候得到的结果并非如此,这只是一个代表而已

the id of this thread is 2
the id of this thread is 8
the id of this thread is 9
the id of this thread is 9
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
the id of this thread is 20
看到这个结果有没有感觉到有什么不对呢?可能你会感觉到很纳闷,怎么出现了那么多的id=20的结果呢?其实这个认真分析一下并不难理解:
首先pthread_create函数传递的是一个指针型的参数,即传递的是一个地址而已,这样在执行for结构时
for(int i=0; i<th_pop; i++)
    {
        pthread_create(&a_thread[i] , NULL , thread_func , &i);
    }
该块快速执行完成,并且将i置为20,故而传递的地址指向的内容为20,同时其它的线程还没来得及执行              int t_id = *(int*)args;,这样就使得多个线程都指向同一个地址,内容为20,解决该问题的一个办法为中for结构中加入sleep(1),这样当sleep时间大于线程函数执行时间,就可以得到一个正确的结果,不过这种办法剥掉了并发性,并不可取,下面我们采用另一种方法。
我们只修改init()函数
void init()
{
    pthread_mutex_init(&mutex, NULL);
    int thread_id[th_pop];
    for(int i=0; i<th_pop; i++)
        thread_id[i] = i;
    for(int i=0; i<th_pop; i++)
    {
        int *t = thread_id +i;
        pthread_create(&a_thread[i] , NULL , thread_func , (void*)t);
    }
    //wait the end of the threads;
    for(int i=0; i<th_pop; i++)
    {
        int res = pthread_join(a_thread[i] , NULL);
        if(res != 0)
            cout<<"the thread id: "<< i<<" ends fail"<<endl;
    }
    pthread_mutex_destroy(&mutex);
    
}
下面输出结果
the id of this thread is 0
the id of this thread is 4
the id of this thread is 2
the id of this thread is 5
the id of this thread is 1
the id of this thread is 3
the id of this thread is 6
the id of this thread is 7
the id of this thread is 8
the id of this thread is 9
the id of this thread is 10
the id of this thread is 11
the id of this thread is 12
the id of this thread is 13
the id of this thread is 14
the id of this thread is 15
the id of this thread is 16
the id of this thread is 17
the id of this thread is 18
the id of this thread is 19

从这个例子中我们应该明白,要避免直接在传递的参数中传递发生改变的量,否则会导致结果不可测


pthread_create()函数参数使用心得

发布时间:2005-01-02 02:03:00  来源: ChinaUnix博客    作者: ChinaUnix博客    点击:2693


线程创建函数:
int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void * (*func)(void *), void *arg);
参数func 表示代一个参数void *,返回值也为void *;
对于void *arg,参数传入,在gcc 3.2.2条件下,以下面两种方式传入都可编译通过。
int ssock;
int TCPechod(int fd);
1.pthread_create(&th, &ta, (void *(*)(void *))TCPechod, (void *)ssock);
2.pthread_create(&th, &ta, (void *(*)(void *))&TCPechod, (void *)&ssock);







pthread_create如何传递多个参数

分类: C语言   98人阅读  评论(0)  收藏  举报

http://zhidao.baidu.com/question/315398992.html

涉及多参数传递给线程的,都需要使用结构体将参数封装后,将结构体指针传给线程

定义一个结构体
struct mypara
{
       var para1;//参数1
       var para2;//参数2
}
将这个结构体指针,作为void *形参的实际参数传递
struct mypara pstru;
pthread_create(&ntid, NULL, thr_fn,& (pstru));
函数中需要定义一个mypara类型的结构指针来引用这个参数 
void *thr_fn(void *arg)
{
       mypara *pstru;
       pstru = (* struct mypara) arg;
       pstru->para1;//参数1
       pstru->para2;//参数2

}

pthread_create函数接受的参数只有一个void *型的指针,这就意味着你只能通过结构体封装超过一个以上的参数作为一个整体传递。这是pthread_create函数的接口限定的,别人已经明确表明我只接受一个参数,你硬要塞给他两个肯定会出错了。所以通过结构体这种组合结构变通一下,同样实现了只通过一个参数传递,但通过结构指针对结构数据成员的引用实现多参数的传递

这种用结构体封装多参数的用法不仅仅用在pthread_create函数中,如果你自己设计的函数需要的参数很多〉=5个以上,都可以考虑使用结构体封装,这样对外你的接口很简洁清晰,你的函数的消费者使用起来也很方便,只需要对结构体各个成员赋值即可,避免了参数很多时漏传、误传(参数串位)的问题

结构体内包含结构体完全没有问题,很多应用都这么使用


举例如下:

http://wenku.baidu.com/view/48a302ed6294dd88d0d26b73.html

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<pthread.h>  
  4. #include<errno.h>  
  5. #include<unistd.h>  
  6.   
  7. typedef void* (*fun)(void*);    
  8.   
  9. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  10. static pthread_cond_t recv_over = PTHREAD_COND_INITIALIZER;  
  11. static pthread_cond_t decode_over = PTHREAD_COND_INITIALIZER;  
  12. static pthread_cond_t play_over = PTHREAD_COND_INITIALIZER;  
  13.   
  14. void* receive(void*);  
  15. void* decode(void*);  
  16. void* play(void*);  
  17.   
  18. pthread_t tdec, tplay, trecv;  
  19.   
  20. struct mypara   
  21. {   
  22.     int thread_id;  
  23.     char *thread_name;   
  24. };  
  25.   
  26. int main(int argc, char** argv)  
  27. {  
  28.     struct mypara para;  
  29.     para.thread_id = 1;  
  30.     para.thread_name = "recv";  
  31.       
  32.     int t1 = 0, t2 = 0, t3 = 0;  
  33.     t1 = pthread_create(&trecv, NULL, receive,& (para));  
  34.     if(t1 != 0)  
  35.         printf("Create thread receive error!\n");  
  36.       
  37.     t2 = pthread_create(&tdec, NULL, decode, NULL);  
  38.     if(t2 != 0)  
  39.         printf("Create thread decode error!\n");  
  40.   
  41.     t3 = pthread_create(&tplay, NULL, play, NULL);  
  42.     if(t3 != 0)  
  43.         printf("Create thread play error!\n");  
  44.   
  45.     pthread_join(trecv, NULL);  
  46.     pthread_join(tdec, NULL);  
  47.     pthread_join(tplay, NULL);  
  48.     printf("leave main\n");  
  49.     exit(0);      
  50. }  
  51.   
  52. void* receive(void* arg)  
  53. {  
  54.     printf("Start receive\n");  
  55.     int i = 0;  
  56.     char *s = NULL;  
  57.   
  58.     struct mypara *recv_para;  
  59.     recv_para = (struct mypara *)arg;  
  60.     i = (*recv_para).thread_id;  
  61.     s = (*recv_para).thread_name;  
  62.     printf("NO : %d Name : %s\n",i,s);  
  63.       
  64.     sleep(2);  
  65.     pthread_mutex_lock(&mutex);  
  66.     while (1)  
  67.     {  
  68.         printf("Receiving...\n");  
  69.         sleep(1);  
  70.         pthread_cond_signal(&recv_over);  
  71.         pthread_cond_wait(&decode_over, &mutex);   
  72.     }  
  73.     printf("End receive\n");  
  74.     pthread_exit(0);  
  75. }  
  76.   
  77. void* decode(void* arg)  
  78. {  
  79.     printf("Start decode\n");  
  80.       
  81.     while (1)  
  82.     {  
  83.         pthread_cond_wait(&recv_over, &mutex);   
  84.         printf("Decoding...\n");  
  85.         sleep(1);  
  86.         pthread_cond_broadcast(&decode_over);   //inform player ready to play  
  87.     }  
  88.     printf("End decode\n");  
  89.     pthread_exit(0);  
  90. }  
  91.   
  92.   
  93. void* play(void* arg)  
  94. {  
  95.     int ret;  
  96.     printf("Start play\n");  
  97.   
  98.     while(1)  
  99.     {  
  100.         pthread_cond_wait(&decode_over, &mutex); //wait the signal from decoder  
  101.         printf("Playing...\n");  
  102.         sleep(1);  
  103.     }  
  104.     pthread_mutex_unlock(&mutex);  
  105.     printf("End play\n");  
  106.     pthread_exit(0);  
  107. }  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值