c/c++: 多线程编程基础讲解

c/c++: 多线程编程基础讲解(一)

利用几篇文章简单记录下c与c++多线程编程基础入门的东西,每篇一个程序来记录,备忘。这些章节是由浅入深组织的,赖死狗!

在注释中讲吧,佛楼米!

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pthread.h>//头文件是必须的,符合posix标准使程序可移植众多平台  
  3.   
  4. using namespace std;  
  5.   
  6. #define NUM_THREADS 5  
  7.   
  8. void* say_hello(void* args)//线程的运行函数,必须void*,没说的表示返回通用指针、输入通用指针  
  9. {  
  10.     cout << "hello..." << endl;  
  11. }  
  12.   
  13. int main()  
  14. {  
  15.     pthread_t tids[NUM_THREADS];//定义线程的id变量,多个变量可以声明为数组使用  
  16.     for(int i = 0; i < NUM_THREADS; ++i)  
  17.     {  
  18.         int ret = pthread_create(&tids[i], NULL, say_hello, NULL);//参数依次是:创建的线程id,线程参数,调用函数名,传入的函数参数  
  19.         if (ret != 0)  
  20.         {  
  21.            cout << "pthread_create error: error_code=" << ret << endl;  
  22.         }  
  23.     }  
  24.   
  25.     pthread_exit(NULL);//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;  
  26. }  

编译命令:

g++ -lpthread -o test.out test.cpp

其中调用静态库文件pthread是必须的,然后运行测试,欧文!

[plain]  view plain copy
    1. [cpp@node2 pthread]$ ./ex_create  
    2. hello...  
    3. hello...  
    4. hello...  
    5. hello...  
    6. hello...  
    7. [cpp@node2 pthread]$   

c/c++: 多线程编程基础讲解(二)

在基础一上思考,如果线程调用的函数是在一个类中怎么办?答案是将该函数写成静态成员函数,如下模式就很符合C++的写作模式:

[cpp]  view plain copy
    1. #include <iostream>  
    2. #include <pthread.h>  
    3.   
    4. using namespace std;  
    5.   
    6. #define NUM_THREADS 5  
    7.   
    8. class Hello  
    9. {  
    10.     public:  
    11.   
    12.     static void* say_hello(void* args)//除了多了static关键字,别无异样;  
    13.     {  
    14.         cout << "hello..." << endl;  
    15.     }  
    16. };  
    17.   
    18. int main()  
    19. {  
    20.     pthread_t tids[NUM_THREADS];  
    21.     for(int i = 0; i < NUM_THREADS; ++i)  
    22.     {  
    23.         int ret = pthread_create(&tids[i], NULL, Hello::say_hello, NULL);  
    24.         if (ret != 0)  
    25.         {  
    26.            cout << "pthread_create error: error_code=" << ret << endl;  
    27.         }  
    28.     }  
    29.   
    30.     pthread_exit(NULL);  
    31. }  

c/c++: 多线程编程基础讲解(三)

线程会创建了,如何在线程调用函数时,传入参数呢?则应如下所示:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pthread.h>  
  3.   
  4. using namespace std;  
  5.   
  6. #define NUM_THREADS 5  
  7.   
  8. void* say_hello(void* args)  
  9. {  
  10.     int i = *((int*)args);//对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取;  
  11.     cout << "hello in " << i << endl;  
  12. }  
  13.   
  14. int main()  
  15. {  
  16.     pthread_t tids[NUM_THREADS];  
  17.     cout << "hello in main..." << endl;  
  18.     for(int i = 0; i < NUM_THREADS; ++i)  
  19.     {  
  20.         int ret = pthread_create(&tids[i], NULL, say_hello, (void *)&i);//传入的时候必须强制转换为void* 类型,即无类型指针  
  21.         cout << "Current pthread id =" << tids[i] << endl;//这里学会使用tids数组打印创建的进程id信息;  
  22.         if (ret != 0)  
  23.         {  
  24.            cout << "pthread_create error: error_code=" << ret << endl;  
  25.         }  
  26.     }  
  27.   
  28.     pthread_exit(NULL);  
  29. }  


编译、运行,结果如下:

[plain]  view plain copy
  1. Current pthread id =139671233451792  
  2. Current pthread id =139671222961936  
  3. Current pthread id =139671212472080  
  4. Current pthread id =139671201982224  
  5. Current pthread id =139671191492368  
  6. hello in 4196496  
  7. hello in 4196496  
  8. hello in 4196496  
  9. hello in 4196496  
  10. hello in 4196496  

是否发现了问题?对,i的值没有输出预想的结果,这是因为多线程造成的,主进程在i还未赋值时,线程已经开始跑啦!~

那么下面代码是正确的:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pthread.h>  
  3.   
  4. using namespace std;  
  5.   
  6. #define NUM_THREADS 5  
  7.   
  8. void* say_hello(void* args)  
  9. {  
  10.     cout << "hello in thread " << *((int *)args) << endl;  
  11. }  
  12.   
  13. int main()  
  14. {  
  15.     pthread_t tids[NUM_THREADS];  
  16.     int indexes[NUM_THREADS];//用个数组来保存i的值,就不会变了  
  17.   
  18.     for(int i = 0; i < NUM_THREADS; ++i)  
  19.     {  
  20.         indexes[i] = i;//先保存i的值,在调用线程就不会出现问题了  
  21.         int ret = pthread_create( &tids[i], NULL, say_hello, (void *)&(indexes[i]) );  
  22.         if (ret != 0)  
  23.         {  
  24.            cout << "pthread_create error: error_code=" << ret << endl;  
  25.         }  
  26.     }  
  27.     for (int i = 0; i < NUM_THREADS; ++i)  
  28.         pthread_join(tids[i], NULL);  
  29. }  

编译、运行:(源程序去掉了打印线程id的废话)

[plain]  view plain copy
    1. [cpp@node2 pthread]$ ./ex_create_args_ok  
    2. hello in thread 3  
    3. hello in thread 4  
    4. hello in thread 2  
    5. hello in thread 1  
    6. hello in thread 0  

c/c++: 多线程编程基础讲解(四)

经过前面的几个例子,是不是还少个线程创建时属性参数没有提到,见下文示例:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pthread.h>  
  3.   
  4.   
  5. #include <iostream>  
  6. #include <pthread.h>  
  7.   
  8. using namespace std;  
  9.   
  10. #define NUM_THREADS 5  
  11.   
  12. void* say_hello(void* args)  
  13. {  
  14.     cout << "hello in thread " << *((int *)args) << endl;  
  15.     int status = 10 + *((int *)args);//将参数加10  
  16.     pthread_exit((void*)status);//由于线程创建时候提供了joinable参数,这里可以在退出时添加退出的信息:status供主程序提取该线程的结束信息;  
  17. }  
  18.   
  19. int main()  
  20. {  
  21.     pthread_t tids[NUM_THREADS];  
  22.     int indexes[NUM_THREADS];  
  23.   
  24.     pthread_attr_t attr;//要想创建时加入参数,先声明  
  25.     pthread_attr_init(&attr);//再初始化  
  26.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);//声明、初始化后第三步就是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能,这个深层理解必须通过图示才能解释;参阅其他资料吧  
  27.   
  28.     for(int i = 0; i < NUM_THREADS; ++i)  
  29.     {  
  30.         indexes[i] = i;  
  31.         int ret = pthread_create( &tids[i], &attr, say_hello, (void *)&(indexes[i]) );//这里四个参数都齐全了,更多的配置仍需查阅资料;  
  32.         if (ret != 0)  
  33.         {  
  34.            cout << "pthread_create error: error_code=" << ret << endl;  
  35.         }  
  36.     }  
  37.   
  38.     pthread_attr_destroy(&attr);//参数使用完了就可以销毁了,必须销毁哦,防止内存泄露;  
  39.   
  40.     void *status;  
  41.     for (int i = 0; i < NUM_THREADS; ++i)  
  42.     {  
  43.         int ret = pthread_join(tids[i], &status);//前面创建了线程,这里主程序想要join每个线程后取得每个线程的退出信息status;  
  44.         if (ret != 0)  
  45.         {  
  46.             cout << "pthread_join error: error_code=" << ret << endl;  
  47.         }  
  48.         else  
  49.         {  
  50.             cout << "pthread_join get status: " << (long)status << endl;  
  51.         }  
  52.     }  
  53. }  

编译运行 g++ -lpthread -o ex_join ex_join.cpp

结果:

[plain]  view plain copy
  1. hello in thread 4  
  2. hello in thread 3  
  3. hello in thread 2  
  4. hello in thread 1  
  5. hello in thread 0  
  6. pthread_join get status: 10  
  7. pthread_join get status: 11  
  8. pthread_join get status: 12  
  9. pthread_join get status: 13  
  10. pthread_join get status: 14  

体会一下join的功能吧

c/c++: 多线程编程基础讲解(五)

本篇进入难点了,mutex互斥锁概念,mutex=mutual exclusion的缩写,顺便说一句:以前老师都爱用缩写,也不跟同学说全称,这尼玛能理解深刻么!下文是用法:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pthread.h>//按规矩不能少  
  3.   
  4. using namespace std;  
  5.   
  6. #define NUM_THREADS 5  
  7.   
  8. int sum = 0;//定义个全局变量,让所有线程进行访问,这样就会出现同时写的情况,势必会需要锁机制;  
  9. pthread_mutex_t sum_mutex;  
  10.   
  11. void* say_hello(void* args)  
  12. {  
  13.     cout << "hello in thread " << *((int *)args) << endl;  
  14.     pthread_mutex_lock (&sum_mutex);//修改sum就先加锁,锁被占用就阻塞,直到拿到锁再修改sum;  
  15.     cout << "before sum is " << sum << " in thread " << *((int *)args) << endl;  
  16.     sum += *((int *)args);  
  17.     cout << "after sum is " << sum << " in thread " << *((int *)args) << endl;  
  18.     pthread_mutex_unlock (&sum_mutex);//完事后解锁,释放给其他线程使用;  
  19.   
  20.     pthread_exit(0);//退出随便扔个状态码  
  21. }  
  22.   
  23. int main()  
  24. {  
  25.     pthread_t tids[NUM_THREADS];  
  26.     int indexes[NUM_THREADS];  
  27.     //下三句是设置线程参数没啥可说的  
  28.     pthread_attr_t attr;  
  29.     pthread_attr_init(&attr);  
  30.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);  
  31.   
  32.     pthread_mutex_init (&sum_mutex, NULL);//这句是对锁进行初始化,必须的;  
  33.   
  34.     for(int i = 0; i < NUM_THREADS; ++i)  
  35.     {  
  36.         indexes[i] = i;  
  37.         int ret = pthread_create( &tids[i], &attr, say_hello, (void *)&(indexes[i]) );//5个进程去你们去修改sum吧哈哈;  
  38.         if (ret != 0)  
  39.         {  
  40.            cout << "pthread_create error: error_code=" << ret << endl;  
  41.         }  
  42.     }  
  43.   
  44.     pthread_attr_destroy(&attr);//删除参数变量  
  45.   
  46.     void *status;  
  47.         for (int i = 0; i < NUM_THREADS; ++i)  
  48.     {  
  49.                 int ret = pthread_join(tids[i], &status);  
  50.         if (ret != 0)  
  51.         {  
  52.             cout << "pthread_join error: error_code=" << ret << endl;  
  53.         }  
  54.     }  
  55.   
  56.     cout << "finally sum is " << sum << endl;  
  57.   
  58.     pthread_mutex_destroy(&sum_mutex);//注销锁,可以看出使用pthread内置变量神马的都对应了销毁函数,估计是内存泄露相关的吧;  
  59. }  

惯例:g++ -lpthread -o ex_mutex ex_mutex.cpp

运行:

[cpp]  view plain copy
  1. hello in thread 4  
  2. before sum is 0 in thread 4  
  3. after sum is 4 in thread 4  
  4. hello in thread 3  
  5. before sum is 4 in thread 3  
  6. after sum is 7 in thread 3  
  7. hello in thread 2  
  8. before sum is 7 in thread 2  
  9. after sum is 9 in thread 2  
  10. hello in thread 1  
  11. before sum is 9 in thread 1  
  12. after sum is 10 in thread 1  
  13. hello in thread 0  
  14. before sum is 10 in thread 0  
  15. after sum is 10 in thread 0  
  16. finally sum is 10  

发现个现象,thread4先运行,很诡异吧而i是从0递增的,所以呢多线程的顺序是混乱的,混乱就是正常;只要sum访问及修改是正常的,就达到多线程的目的了,运行顺序不能作为参照;


c/c++: 多线程编程基础讲解(六)

上篇说了下互斥量的用法,今儿说一下条件信号量的用法,这两种多线程变量的用法其实取决于情景,需要体会,见文:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <pthread.h>//带头文件  
  3. #include <stdio.h>  
  4.   
  5. using namespace std;  
  6.   
  7. #define BOUNDARY 5  
  8.   
  9. int tasks = 10;  
  10.   
  11. pthread_mutex_t tasks_mutex;//因为两个线程要修改一个全局变量,需要互斥量;  
  12. pthread_cond_t tasks_cond;//因为两个线程间有条件关系:当tasks>5时,hello2处理它,处理一次减少1;反之hello1处理,直到tasks减为零;  
  13.   
  14. void* say_hello2(void* args)//hello2处理函数  
  15. {  
  16.     pthread_t pid = pthread_self();//打印当前线程id便于跟踪  
  17.     cout << "["<< pid << "] hello in thread " << *((int*)args) << endl;  
  18.   
  19.     bool is_signaled = false;//随便一个标志位  
  20.     while(1)//无限循环  
  21.     {  
  22.         pthread_mutex_lock(&tasks_mutex);//要修改了,加锁  
  23.         if (tasks > BOUNDARY)//>5才修改  
  24.         {  
  25.             cout << "["<< pid << "] take task:  "<< tasks << " in thread "<< *((int*)args) << endl;  
  26.             --tasks;//减少1  
  27.         }  
  28.         else if (!is_signaled)  
  29.         {  
  30.             cout << "["<< pid << "] pthread_cond_signal in thread " << *((int*)args) << endl;  
  31.             pthread_cond_signal(&tasks_cond);//表明已经不是>5了告诉hello1进程去处理:发送信号;  
  32.             is_signaled = true;//表示信号已经发送了  
  33.         }  
  34.         pthread_mutex_unlock(&tasks_mutex);//操作完解锁  
  35.   
  36.         if (tasks == 0) break;//必须等待tasks全部减为零即hello1完成操作,才跳出循环结束这个进程  
  37.     }  
  38. }  
[cpp]  view plain copy
  1. <p>void* say_hello1(void* args)//<=5处理函数  
  2. {  
  3.     pthread_t pid = pthread_self();  
  4.     cout << "["<< pid << "] hello in thread " << *((int*)args) << endl;</p><p>    while(1)  
  5.     {  
  6.         pthread_mutex_lock(&tasks_mutex);  
  7.         if (tasks > BOUNDARY)//如果>5说明需要hello2处理,那么该线程就需要等待  
  8.         {  
  9.             cout << "["<< pid << "] pthread_cond_wait in thread " << *((int*)args) << endl;  
  10.             pthread_cond_wait(&tasks_cond, &tasks_mutex);//等待信号量生效,当hello2发出信号,这里就跳出wait,执行后续;  
  11.         }  
  12.         else  
  13.         {  
  14.             cout << "["<< pid << "] take task:  "<< tasks << " in thread "<< *((int*)args) << endl;  
  15.             --tasks;//<=5就--  
  16.         }  
  17.         pthread_mutex_unlock(&tasks_mutex);</p><p>        if (tasks == 0) break;//为零时退出,同hello2一样  
  18.     }</p><p>}</p><p>int main()  
  19. {  
  20.     pthread_attr_t attr;//线程创建为joinable的,使得主进程可以和两个线程同步,两个线程完成工作退出后,主进程再退出;  
  21.     pthread_attr_init(&attr);  
  22.     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);</p><p>    pthread_mutex_init(&tasks_mutex, NULL);//初始化互斥量  
  23.     pthread_cond_init(&tasks_cond, NULL);//初始化条件信号量</p><p>    pthread_t tid1, tid2;//用于保存两个线程的id号  
  24.     int index1 = 1;  
  25.     int ret = pthread_create( &tid1, &attr, say_hello1, (void *)&index1);  
  26.     if (ret != 0)  
  27.     {  
  28.         cout << "pthread_create error: error_code=" << ret << endl;  
  29.     }</p><p>    int index2 = 2;  
  30.     ret = pthread_create( &tid2, &attr, say_hello2, (void *)&index2);  
  31.     if (ret != 0)  
  32.     {  
  33.         cout << "pthread_create error: error_code=" << ret << endl;  
  34.     }</p><p> </p><p>    pthread_join(tid1, NULL);//连接两个线程  
  35.     pthread_join(tid2, NULL);</p><p>    pthread_attr_destroy(&attr);//该销毁的销毁  
  36.     pthread_mutex_destroy(&tasks_mutex);  
  37.     pthread_cond_destroy(&tasks_cond);</p><p>    //正常退出  
  38. }</p><p> </p>  

惯例:g++ -lpthread -o ex_cond ex_cond.cpp

执行结果:

[cpp]  view plain copy
    1. [cpp@node2 pthread]$ ./ex_cond   
    2. [140009886947088] hello in thread 2  
    3. [140009886947088] take task:  10 in thread 2  
    4. [140009886947088] take task:  9 in thread 2  
    5. [140009886947088] take task:  8 in thread 2  
    6. [140009886947088] take task:  7 in thread 2  
    7. [140009886947088] take task:  6 in thread 2  
    8. [140009886947088] pthread_cond_signal in thread 2  
    9. [140009897436944] hello in thread 1  
    10. [140009897436944] take task:  5 in thread 1  
    11. [140009897436944] take task:  4 in thread 1  
    12. [140009897436944] take task:  3 in thread 1  
    13. [140009897436944] take task:  2 in thread 1  
    14. [140009897436944] take task:  1 in thread 1  





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值