当前位置:   article > 正文

程序设计:C++ 多进程、多线程原理和一个多进程、多线程框架_c++多进程

c++多进程

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


        并发编程和异步编程是程序员的基本技能,各种高级语言也开发出了一些高级但晦涩的机制(比如C#的await/async)。并发编程主要是指多进程、多线程和交替执行,异步编程则是指多个并发执行任务之间的交互。

目录

并发和异步的技术介绍

多进程的基本技术

多进程框架代码

多线程的基本技术(C++11)

多线程框架代码


并发和异步的技术介绍

        并发的几种方式的区别:

  • 多进程 进程之间地址空间隔离,不能共享数据,需要通过IPC、文件、信号等进程间交互机制交互,交互困难但容易调试,程序也比较可靠,一个进程异常不会影响其它进程。
  • 多线程 共享内存地址空间,可以随意访问全部数据,必须正确互斥才能避免数据被破坏。
  • 交替执行 比如await/async,所有后台任务都是在同一个线程里执行的,这依赖一套复杂的接口,比较晦涩。一般采用自定义接口,轮询执行的方式,这要求每个任务都比较小,执行时间可控,比如redis。

        任务交互的不同方式:

  • 内存 速度最快,只适用于多线程,有并发冲突问题。
  • 共享内存 速度最快,适用于多进程,有并发冲突问题,而且不可以动态增长。
  • 信号、信号量 不能携带数据,只能用于控制,用起来费劲
  • SOCKET、管道 可靠,但是和服务器、客户端编程一样,复杂
  • 文件 简单,不用学习新技术,有并发冲突问题

多进程的基本技术

  • fork() 复制进程自身,双返回,在父进程返回子进程的pid,在子进程返回0
  • waitpid() 获取进程状态,等待进程结束
  • WIFEXITED() 判断进程状态码是否表示进程是正常退出
  • 获取返回值 获取返回值的宏兼容性并不好!具体看后面的源代码

        这几个技术的详细介绍应该很容易找到,我们关心的是如何写出一个套路代码。以下是一个多进程的框架程序,能够起一组子进程并等待所有子进程结束。

多进程框架代码

        首先定义子进程的执行代码的接口:

  1. class IChildProcess
  2. {
  3. public:
  4. virtual int doChildProcess(long max_process, long i_process) = 0;
  5. };

        纯虚函数doChildProcess是子进程的功能入口,以最大进程数和子进程序号为参数,子进程序号从0开始。按照C语言的习惯这个接口应该加一个void*参数供调用方传入来控制子进程的功能,但是这里写成了虚函数,直接在类里面定义就可以了,这是C++比C方便的地方。

        多进程框架:

  1. int SimpleMultiProcess(IChildProcess * pChild,long max_process, char const* title)
  2. {
  3. pChild->isThread = false;
  4. thelog << "开始执行" << title << " 进程 " << max_process << endi;
  5. time_t t1 = time(NULL);
  6. long i_p;
  7. for (i_p = 0; i_p < max_process; ++i_p)
  8. {
  9. pid_t pid = fork();
  10. if (pid < 0)
  11. {
  12. thelog << "fork失败" << ende;
  13. return __LINE__;
  14. }
  15. else if (0 == pid)//子进程
  16. {
  17. exit(pChild->doChildProcess(max_process, i_p));//子进程在这里通过exit结束
  18. }
  19. }
  20. int status;
  21. int ret = 0;
  22. stringstream msg;
  23. for (i_p = 0; i_p < max_process; ++i_p)
  24. {
  25. pid_t pid = waitpid(-1, &status, 0);//等待任何一个子进程结束,原则上这个循环写法有问题,如果程序还有其它地方创建子进程则这里程序行为就不符合预期
  26. if (-1 == pid)
  27. {
  28. thelog << "waitpid 出错 " << strerror(errno) << ende;
  29. return __LINE__;
  30. }
  31. else
  32. {
  33. if (WIFEXITED(status))
  34. {
  35. int tmpret = (0xFF00 & status) / 256;//WEXITSTATUS宏无法识别
  36. if (0 != tmpret)
  37. {
  38. msg << "进程 " << pid << " 出错,返回值 " << tmpret << " 状态码 " << status << endl;
  39. ret = tmpret;
  40. }
  41. }
  42. else
  43. {
  44. ret = 2;
  45. msg << "进程 " << pid << " 异常,状态码 " << status << endl;
  46. }
  47. }
  48. }
  49. int timespan = time(NULL) - t1;
  50. if (msg.str().size() != 0)thelog << msg.str() << ende;
  51. thelog << "执行" << title << "完成 进程 " << max_process << " 总用时 " << timespan << "/秒" << endi;
  52. return ret;
  53. }

        以执行代码的接口为参数,传入最大进程数,title没什么用,只是用来在日志里显示。        

多线程的基本技术(C++11)

        线程和原子大概是C++11最棒的新功能了吧。

  • std::thread 构造函数直接创建子线程
  • std::thread.join 等待子线程结束,join的意思是“合并”,两个线程变成一个

        为啥以前的线程库都那么复杂啊?

多线程框架代码

        仍然是上面多进程的执行代码的接口:

  1. class IChildProcess
  2. {
  3. public:
  4. virtual int doChildProcess(long max_process, long i_process) = 0;
  5. };

        框架代码,比多进程简单:

  1. int SimpleMultiThread(IChildProcess* pChild, long max_thread, char const* title)
  2. {
  3. pChild->isThread = true;
  4. thelog << "开始执行" << title << " 线程 " << max_thread << endi;
  5. time_t t1 = time(NULL);
  6. long i_p;
  7. vector<thread *> threads;
  8. for (i_p = 0; i_p < max_thread; ++i_p)
  9. {
  10. threads.push_back(new thread(doChildThread, pChild, max_thread, i_p));
  11. }
  12. for (auto & v:threads)
  13. {
  14. v->join();
  15. }
  16. int timespan = time(NULL) - t1;
  17. thelog << "执行" << title << "完成 线程 " << max_thread << " 总用时 " << timespan << "/秒" << endi;
  18. return 0;
  19. }

        这个代码基本就是一目了然了。当然了,多线程最大困难在于安全地交互。


(这里是结束)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/1014268
推荐阅读
相关标签
  

闽ICP备14008679号