分享

Linux 平台多线程编程实例

 看风景D人 2016-10-04

最近在优化一个图像处理算法,算法中要对于不同的图片做相同的图像处理算法,不同图片之间的处理数据时独立的,因而很自然的想到利用多线程优化算法。

下面是一些学习代码

Linux下面的多线编程需要包含明白以下几点:

1 pthread_t
       pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
  typedef unsigned long int pthread_t;
  它是一个线程的标识符。

2 pthread_create
      函数pthread_create用来创建一个线程,它的原型为:
      extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
  void *(*__start_routine) (void *), void *__arg));
       第一个参数为指向线程标识符的指针;

第二个参数用来设置线程属性;

第三个参数是线程运行函数的起始地址;

最后一个参数是运行函数的参数。

这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。

第二个参数我们也设为空指针,这样将生成默认属性的线程。

对线程属性的设定和修改我们将在下一节 阐述。

当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。

前者表示系统限制创建新的线程,例 如线程数目过多了;

后者表示第二个参数代表的线程属性值非法。

创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,

原来的线程则继续运行下一行 代码。

3 pthread_join 和pthread_exit
       函数pthread_join用来等待一个线程的结束。函数原型为:
  extern int pthread_join __P ((pthread_t __th, void **__thread_return));
       第一个参数为被等待的线程标识符;

第二个参数为一个用户定义的指针;

它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将 一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是像我们上面的例子一样,函数结束了,调用它的 线程也就结束了;

另一种方式是通过函数pthread_exit来实现。它的函数原型为:
  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
   唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。

最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线 程则返回错误代码ESRCH。
  在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join和pthread_exit。

4 互斥锁相关
互斥锁用来保证一段时间内只有一个线程在执行一段代码。

pthread_mutex_init函数用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值, PTHREAD_PROCESS_PRIVATE;

PTHREAD_PROCESS_SHARED;

前者用来不同进程中的线程同步,后者用于同步本进程的 不同线程。

在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有

PTHREAD_MUTEX_NORMAL、

PTHREAD_MUTEX_ERRORCHECK、

PTHREAD_MUTEX_RECURSIVE和

PTHREAD _MUTEX_DEFAULT。

它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。
pthread_mutex_lock和 pthread_mutex_unlock以及pthread_delay_np
pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只 能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一 个线程释放此互斥锁。

 

二 简单的代码实例

 

  1. #include <pthread.h>  
  2. #include <iostream>  
  3. #include <unistd.h>  
  4. #include <sys/time.h>  
  5. #include <string>  
  6. using namespace std;  
  7.   
  8. pthread_t thread[2];  
  9. pthread_mutex_t mut;  
  10. double array[10][10];  
  11.   
  12. double funtime(const string &str)  
  13. {  
  14.     struct timeval tpstart,tpend;  
  15.     double timeuse;  
  16.     gettimeofday(&tpstart,NULL);  
  17.   
  18.     int loop = 1000000;  
  19.     while(loop--)  
  20.     {  
  21.         for (int i = 0; i < 10; ++i)  
  22.         {  
  23.             for(int j = 0; j < 10;++j)  
  24.             {  
  25.                 array[i][j] = 0.63;  
  26.                 array[i][j] /= 0.96;  
  27.             }  
  28.         }  
  29.     }  
  30.     gettimeofday(&tpend,NULL);  
  31.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;  
  32.     timeuse/=1000;  
  33.     cout << str << " timeuse: " << timeuse << " ms" << endl;  
  34.     return timeuse;  
  35. }  
  36.   
  37.   
  38. void *thread1(void *args)  
  39. {  
  40.     cout<< "thread1 : I'm thread 1" << endl;  
  41.     funtime("thread1 fun");  
  42.     cout<< "thread1 :main function is waiting me" << endl;  
  43.     pthread_exit(NULL);  
  44. }  
  45.   
  46. void *thread2(void *args)  
  47. {  
  48.     cout<< "thread2 : I'm thread 2" << endl;  
  49.     funtime("thread2 fun");  
  50.     cout<< "thread2 :main function is waiting me" << endl;  
  51.     pthread_exit(NULL);  
  52. }  
  53.   
  54. void thread_create(void)  
  55. {  
  56.     int temp;  
  57.     memset(&thread, 0, sizeof(thread));            
  58.     if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0)       
  59.         cout<<"thread1 is created failed" << endl;  
  60.     else  
  61.         cout<<"thread1 is created " << endl;  
  62.     if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0)    
  63.         cout<<"thread2 created failed" << endl;  
  64.     else  
  65.         cout<<"thread2 is created" << endl;  
  66. }  
  67.   
  68. void thread_wait(void)  
  69. {  
  70.     if(thread[0] !=0)  
  71.     {       
  72.         pthread_join(thread[0],NULL);  
  73.         cout<<("thread1 is end \n");  
  74.     }  
  75.     if(thread[1] !=0)   
  76.     {    
  77.         pthread_join(thread[1],NULL);  
  78.         cout<<("thread2 is end \n");  
  79.     }  
  80. }  
  81.   
  82.   
  83. int main()  
  84. {  
  85.     struct timeval tpstart,tpend;  
  86.     double timeuse;  
  87.     gettimeofday(&tpstart,NULL);  
  88.   
  89.     pthread_mutex_init(&mut,NULL);  
  90.     cout<<"main function is created thread" << endl;  
  91.     thread_create();  
  92.     cout<<"main function is waiting thread" << endl;  
  93.     thread_wait();  
  94.   
  95.     gettimeofday(&tpend,NULL);  
  96.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;  
  97.     timeuse/=1000;  
  98.     cout << "MulThread " << " timeuse: " << timeuse << " ms" << endl;  
  99.   
  100.     cout << "signle thread time using: " <<   
  101.     funtime("main fun") + funtime("main fun") << " ms" << endl;  
  102.   
  103.     return 0;  
  104. }  



测试运行:

linux 平台下面编译时需要加上 -lpthrad 如上面的代码test.cpp , 编译命令:

  g++  -o test test.cpp -lpthread

可以看到两个线程都开始运行了,执行一次funtime函数需要花费1000ms左右的时间,单线程执行两次需要花费1953ms,而多线程利用两个线程并行执行花费的时间为1031ms,总的时间缩短了接近一倍。

为了验证多线程程序的执行结果是否正确,可以打印出矩阵观察。

修改代码:

  1. #include <pthread.h>  
  2. #include <iostream>  
  3. #include <unistd.h>  
  4. #include <sys/time.h>  
  5. #include <string>  
  6. using namespace std;  
  7.   
  8. pthread_t thread[2];  
  9. pthread_mutex_t mut;  
  10.   
  11. double array1[10][10];  
  12. double array2[10][10];  
  13. double array0[10][10];  
  14.   
  15. double funtime(const string &str, const int index)  
  16. {  
  17.     struct timeval tpstart,tpend;  
  18.     double timeuse;  
  19.     gettimeofday(&tpstart,NULL);  
  20.   
  21.     int loop = 1000000;  
  22.     while(loop--)  
  23.     {  
  24.         for (int i = 0; i < 10; ++i)  
  25.         {  
  26.             for(int j = 0; j < 10;++j)  
  27.             {  
  28.                 if (index == 1)  
  29.                 {  
  30.                     array1[i][j] = 1;  
  31.                     array1[i][j] /= 2;  
  32.                 }else if(index == 2)  
  33.                 {  
  34.                     array2[i][j] = 2;  
  35.                     array2[i][j] /= 2;  
  36.                 }else  
  37.                 {  
  38.                     array0[i][j] = 3;  
  39.                     array0[i][j] /= 2;  
  40.                 }  
  41.             }  
  42.         }  
  43.     }  
  44.   
  45.     gettimeofday(&tpend,NULL);  
  46.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;  
  47.     timeuse/=1000;  
  48.     cout << str << " timeuse: " << timeuse << " ms" << endl;  
  49.     return timeuse;  
  50. }  
  51.   
  52.   
  53. void *thread1(void *args)  
  54. {  
  55.     cout<< "thread1 : I'm thread 1" << endl;  
  56.     funtime("thread1 fun",1);  
  57.     cout<< "thread1 :main function is waiting me" << endl;  
  58.     pthread_exit(NULL);  
  59. }  
  60.   
  61. void *thread2(void *args)  
  62. {  
  63.     cout<< "thread2 : I'm thread 2" << endl;  
  64.     funtime("thread2 fun",2);  
  65.     cout<< "thread2 :main function is waiting me" << endl;  
  66.     pthread_exit(NULL);  
  67. }  
  68.   
  69. void thread_create(void)  
  70. {  
  71.     int temp;  
  72.     memset(&thread, 0, sizeof(thread));            
  73.     if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0)       
  74.         cout<<"thread1 is created failed" << endl;  
  75.     else  
  76.         cout<<"thread1 is created " << endl;  
  77.     if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0)    
  78.         cout<<"thread2 created failed" << endl;  
  79.     else  
  80.         cout<<"thread2 is created" << endl;;  
  81. }  
  82.   
  83. void thread_wait(void)  
  84. {  
  85.     if(thread[0] !=0)  
  86.     {       
  87.         pthread_join(thread[0],NULL);  
  88.         cout<<("thread1 is end \n");  
  89.     }  
  90.     if(thread[1] !=0)   
  91.     {    
  92.         pthread_join(thread[1],NULL);  
  93.         cout<<("thread2 is end \n");  
  94.     }  
  95. }  
  96.   
  97.   
  98. void printresult()  
  99. {  
  100.     cout << "array1" << endl;  
  101.     for (int i = 0; i < 10; ++i)  
  102.     {  
  103.         for (int j = 0; j < 10; ++j)  
  104.         {  
  105.             cout << " " << array1[i][j];  
  106.         }  
  107.         cout << endl;  
  108.     }  
  109.   
  110.     cout <<"array2" <<endl;  
  111.     for (int i = 0; i < 10; ++i)  
  112.     {  
  113.         for (int j = 0; j < 10; ++j)  
  114.         {  
  115.             cout << " " << array2[i][j];  
  116.         }  
  117.         cout << endl;  
  118.     }  
  119.   
  120.     cout <<"array0" <<endl;  
  121.     for (int i = 0; i < 10; ++i)  
  122.     {  
  123.         for (int j = 0; j < 10; ++j)  
  124.         {  
  125.             cout << " " << array0[i][j];  
  126.         }  
  127.         cout << endl;  
  128.     }  
  129.   
  130. }  
  131.   
  132.   
  133. int main()  
  134. {  
  135.     struct timeval tpstart,tpend;  
  136.     double timeuse;  
  137.     gettimeofday(&tpstart,NULL);  
  138.   
  139.     pthread_mutex_init(&mut,NULL);  
  140.     cout<<"main function is created thread" << endl;  
  141.     thread_create();  
  142.     cout<<"main function is waiting thread" << endl;  
  143.     thread_wait();  
  144.   
  145.     gettimeofday(&tpend,NULL);  
  146.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;  
  147.     timeuse/=1000;  
  148.     cout << "MulThread " << " timeuse: " << timeuse << " ms" << endl;  
  149.   
  150.     cout << "signle thread time using: " <<   
  151.     funtime("main fun",0) + funtime("main fun",0) << " ms" << endl;  
  152.   
  153.     printresult();  
  154.   
  155.     return 0;  
  156. }  

从中可以看出线程执行结果是正确的

三 线程函数参数传递

funtime("thread1 fun",1);

funtime函数为线程调用中的主要执行函数,包含两个参数,都被写死了,实际中会有很多限制,因此需要考虑从线程的入口处传入参数。

前面的代码中,pthread_create(&thread[0], NULL, thread1, NULL)) != 0)中,第四个参数是线程执行函数需要传入的参数,在线程函数中void *thread1(void *args)中,只能允许传入一个参数,如果要传入多个参数,需要使用结构体,将多个参数组合为一个结构体一起传入。实例如下:

代码如下

  1. #include <pthread.h>  
  2. #include <iostream>  
  3. #include <unistd.h>  
  4. #include <sys/time.h>  
  5. #include <string>  
  6. using namespace std;  
  7.   
  8. //thread function pamter  
  9. struct theradPamter{  
  10.     int threadindex;  
  11.     string str;  
  12. };  
  13.   
  14. pthread_t thread[2];  
  15. pthread_mutex_t mut;  
  16.   
  17. double array1[10][10];  
  18. double array2[10][10];  
  19. double array0[10][10];  
  20.   
  21.   
  22. double funtime(const string &str, const int index)  
  23. {  
  24.     struct timeval tpstart,tpend;  
  25.     double timeuse;  
  26.     gettimeofday(&tpstart,NULL);  
  27.   
  28.     int loop = 1000000;  
  29.     while(loop--)  
  30.     {  
  31.         for (int i = 0; i < 10; ++i)  
  32.         {  
  33.             for(int j = 0; j < 10;++j)  
  34.             {  
  35.                 if (index == 1)  
  36.                 {  
  37.                     array1[i][j] = 1;  
  38.                     array1[i][j] /= 2;  
  39.                 }else if(index == 2)  
  40.                 {  
  41.                     array2[i][j] = 2;  
  42.                     array2[i][j] /= 2;  
  43.                 }else  
  44.                 {  
  45.                     array0[i][j] = 3;  
  46.                     array0[i][j] /= 2;  
  47.                 }  
  48.             }  
  49.         }  
  50.     }  
  51.   
  52.     gettimeofday(&tpend,NULL);  
  53.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;  
  54.     timeuse/=1000;  
  55.     cout << str << " timeuse: " << timeuse << " ms" << endl;  
  56.     return timeuse;  
  57. }  
  58.   
  59.   
  60. void *thread1(void *args)  
  61. {  
  62.     cout<< "thread1 : I'm thread 1" << endl;  
  63.     theradPamter *tp = (theradPamter *) args;  
  64.     funtime(tp->str, tp->threadindex);  
  65.     cout<< "thread1 :main function is waiting me" << endl;  
  66.     pthread_exit(NULL);  
  67. }  
  68.   
  69. void *thread2(void *args)  
  70. {  
  71.     cout<< "thread2 : I'm thread 2" << endl;  
  72.     theradPamter *tp = (theradPamter *) args;  
  73.     funtime(tp->str, tp->threadindex);  
  74.     cout<< "thread2 :main function is waiting me" << endl;  
  75.     pthread_exit(NULL);  
  76. }  
  77.   
  78. void thread_create(theradPamter *tp1, theradPamter *tp2)  
  79. {  
  80.     int temp;  
  81.     memset(&thread, 0, sizeof(thread));  
  82.       
  83.     if((temp = pthread_create(&thread[0], NULL, thread1, (void *)tp1)) != 0)       
  84.         cout<<"thread1 is created failed" << endl;  
  85.     else  
  86.         cout<<"thread1 is created " << endl;  
  87.     if((temp = pthread_create(&thread[1], NULL, thread2, (void *)tp2)) != 0)    
  88.         cout<<"thread2 created failed" << endl;  
  89.     else  
  90.         cout<<"thread2 is created" << endl;  
  91.   
  92. }  
  93.   
  94. void thread_wait(void)  
  95. {  
  96.     if(thread[0] !=0)  
  97.     {       
  98.         pthread_join(thread[0],NULL);  
  99.         cout<<("thread1 is end \n");  
  100.     }  
  101.     if(thread[1] !=0)   
  102.     {    
  103.         pthread_join(thread[1],NULL);  
  104.         cout<<("thread2 is end \n");  
  105.     }  
  106. }  
  107.   
  108.   
  109. void printresult()  
  110. {  
  111.     cout << "array1" << endl;  
  112.     for (int i = 0; i < 10; ++i)  
  113.     {  
  114.         for (int j = 0; j < 10; ++j)  
  115.         {  
  116.             cout << " " << array1[i][j];  
  117.         }  
  118.         cout << endl;  
  119.     }  
  120.   
  121.     cout <<"array2" <<endl;  
  122.     for (int i = 0; i < 10; ++i)  
  123.     {  
  124.         for (int j = 0; j < 10; ++j)  
  125.         {  
  126.             cout << " " << array2[i][j];  
  127.         }  
  128.         cout << endl;  
  129.     }  
  130.   
  131.     cout <<"array0" <<endl;  
  132.     for (int i = 0; i < 10; ++i)  
  133.     {  
  134.         for (int j = 0; j < 10; ++j)  
  135.         {  
  136.             cout << " " << array0[i][j];  
  137.         }  
  138.         cout << endl;  
  139.     }  
  140.   
  141. }  
  142.   
  143.   
  144. int main()  
  145. {  
  146.     struct timeval tpstart,tpend;  
  147.     double timeuse;  
  148.     gettimeofday(&tpstart,NULL);  
  149.   
  150.     pthread_mutex_init(&mut,NULL);  
  151.     cout<<"main function is created thread" << endl;  
  152.   
  153.     theradPamter *tp1 = new theradPamter;  
  154.     theradPamter *tp2 = new theradPamter;    
  155.     tp1->threadindex = 1;  
  156.     tp1->str = "thread1 fun";    
  157.     tp2->threadindex = 2;  
  158.     tp2->str = "thread2 fun";    
  159.   
  160.     thread_create(tp1, tp2);  
  161.   
  162.     cout<<"main function is waiting thread" << endl;  
  163.     thread_wait();  
  164.   
  165.     delete tp1;  
  166.     tp1 = NULL;  
  167.     delete tp2;  
  168.     tp2 = NULL;  
  169.   
  170.     gettimeofday(&tpend,NULL);  
  171.     timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;  
  172.     timeuse/=1000;  
  173.     cout << "MulThread " << " timeuse: " << timeuse << " ms" << endl;  
  174.   
  175.     cout << "signle thread time using: " <<   
  176.     funtime("main fun",0) + funtime("main fun",0) << " ms" << endl;  
  177.   
  178.     printresult();  
  179.   
  180.     return 0;  
  181. }  

线程函数的参数需要传入一个void *类型的指针,因此需要在传递时,将tp指针强制转化为void*类型,传入 thread1(void *args) 中

在thread1(void *args)函数内,使用该指针时,在强制类型转换为threadPamter类型。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多