c++11多线程

清疚 2022-09-02 12:46 309阅读 0赞

文章目录

  • 对多线程的支持
    • 在 Windows 上
    • 在 Linux 下
    • c++11多线程

c++11多线程

对多线程的支持

我们来看一个稍微复杂一点的例子。

在 C++11 之前,由于 C++98/03 本身缺乏对线程和线程同步原语的支持,我们要写一个生产者消费者逻辑要这么写。

在 Windows 上

代码

  1. /** * RecvMsgTask.h */
  2. class CRecvMsgTask : public CThreadPoolTask
  3. {
  4. public:
  5. CRecvMsgTask(void);
  6. ~CRecvMsgTask(void);
  7. public:
  8. virtual int Run();
  9. virtual int Stop();
  10. virtual void TaskFinish();
  11. BOOL AddMsgData(CBuffer* lpMsgData);
  12. private:
  13. BOOL HandleMsg(CBuffer* lpMsg);
  14. private:
  15. HANDLE m_hEvent;
  16. CRITICAL_SECTION m_csItem;
  17. HANDLE m_hSemaphore;
  18. std::vector<CBuffer*> m_arrItem;
  19. };
  20. /** * RecvMsgTask.cpp */
  21. CRecvMsgTask::CRecvMsgTask(void)
  22. {
  23. ::InitializeCriticalSection(&m_csItem);
  24. m_hSemaphore = ::CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
  25. m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
  26. }
  27. CRecvMsgTask::~CRecvMsgTask(void)
  28. {
  29. ::DeleteCriticalSection(&m_csItem);
  30. if (m_hSemaphore != NULL)
  31. {
  32. ::CloseHandle(m_hSemaphore);
  33. m_hSemaphore = NULL;
  34. }
  35. if (m_hEvent != NULL)
  36. {
  37. ::CloseHandle(m_hEvent);
  38. m_hEvent = NULL;
  39. }
  40. }
  41. int CRecvMsgTask::Run()
  42. {
  43. HANDLE hWaitEvent[2];
  44. DWORD dwIndex;
  45. CBuffer * lpMsg;
  46. hWaitEvent[0] = m_hEvent;
  47. hWaitEvent[1] = m_hSemaphore;
  48. while (1)
  49. {
  50. dwIndex = ::WaitForMultipleObjects(2, hWaitEvent, FALSE, INFINITE);
  51. if (dwIndex == WAIT_OBJECT_0)
  52. break;
  53. lpMsg = NULL;
  54. ::EnterCriticalSection(&m_csItem);
  55. if (m_arrItem.size() > 0)
  56. {
  57. //消费者从队列m_arrItem中取出任务执行
  58. lpMsg = m_arrItem[0];
  59. m_arrItem.erase(m_arrItem.begin() + 0);
  60. }
  61. ::LeaveCriticalSection(&m_csItem);
  62. if (NULL == lpMsg)
  63. continue;
  64. //处理任务
  65. HandleMsg(lpMsg);
  66. delete lpMsg;
  67. }
  68. return 0;
  69. }
  70. int CRecvMsgTask::Stop()
  71. {
  72. m_HttpClient.SetCancalEvent();
  73. ::SetEvent(m_hEvent);
  74. return 0;
  75. }
  76. void CRecvMsgTask::TaskFinish()
  77. {
  78. }
  79. //生产者调用这个方法将Task放入队列m_arrItem中
  80. BOOL CRecvMsgTask::AddMsgData(CBuffer * lpMsgData)
  81. {
  82. if (NULL == lpMsgData)
  83. return FALSE;
  84. ::EnterCriticalSection(&m_csItem);
  85. m_arrItem.push_back(lpMsgData);
  86. ::LeaveCriticalSection(&m_csItem);
  87. ::ReleaseSemaphore(m_hSemaphore, 1, NULL);
  88. return TRUE;
  89. }

在 Linux 下

代码

  1. #include <pthread.h>
  2. #include <errno.h>
  3. #include <unistd.h>
  4. #include <list>
  5. #include <semaphore.h>
  6. #include <iostream>
  7. class Task
  8. {
  9. public:
  10. Task(int taskID)
  11. {
  12. this->taskID = taskID;
  13. }
  14. void doTask()
  15. {
  16. std::cout << "handle a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
  17. }
  18. private:
  19. int taskID;
  20. };
  21. pthread_mutex_t mymutex;
  22. std::list<Task*> tasks;
  23. pthread_cond_t mycv;
  24. void* consumer_thread(void* param)
  25. {
  26. Task* pTask = NULL;
  27. while (true)
  28. {
  29. pthread_mutex_lock(&mymutex);
  30. while (tasks.empty())
  31. {
  32. //如果获得了互斥锁,但是条件不合适的话,pthread_cond_wait会释放锁,不往下执行。
  33. //当发生变化后,条件合适,pthread_cond_wait将直接获得锁。
  34. pthread_cond_wait(&mycv, &mymutex);
  35. }
  36. pTask = tasks.front();
  37. tasks.pop_front();
  38. pthread_mutex_unlock(&mymutex);
  39. if (pTask == NULL)
  40. continue;
  41. pTask->doTask();
  42. delete pTask;
  43. pTask = NULL;
  44. }
  45. return NULL;
  46. }
  47. void* producer_thread(void* param)
  48. {
  49. int taskID = 0;
  50. Task* pTask = NULL;
  51. while (true)
  52. {
  53. pTask = new Task(taskID);
  54. pthread_mutex_lock(&mymutex);
  55. tasks.push_back(pTask);
  56. std::cout << "produce a task, taskID: " << taskID << ", threadID: " << pthread_self() << std::endl;
  57. pthread_mutex_unlock(&mymutex);
  58. //释放信号量,通知消费者线程
  59. pthread_cond_signal(&mycv);
  60. taskID ++;
  61. //休眠1秒
  62. sleep(1);
  63. }
  64. return NULL;
  65. }
  66. int main()
  67. {
  68. pthread_mutex_init(&mymutex, NULL);
  69. pthread_cond_init(&mycv, NULL);
  70. //创建5个消费者线程
  71. pthread_t consumerThreadID[5];
  72. for (int i = 0; i < 5; ++i)
  73. pthread_create(&consumerThreadID[i], NULL, consumer_thread, NULL);
  74. //创建一个生产者线程
  75. pthread_t producerThreadID;
  76. pthread_create(&producerThreadID, NULL, producer_thread, NULL);
  77. pthread_join(producerThreadID, NULL);
  78. for (int i = 0; i < 5; ++i)
  79. pthread_join(consumerThreadID[i], NULL);
  80. pthread_cond_destroy(&mycv);
  81. pthread_mutex_destroy(&mymutex);
  82. return 0;
  83. }

怎么样?上述代码如果对于新手来说,望而却步。

为了实现这样的功能在 Windows 上你需要掌握线程如何创建、线程同步对象 CriticalSection、Event、Semaphore、WaitForSingleObject/WaitForMultipleObjects 等操作系统对象和 API。

在 Linux 上需要掌握线程创建,你需要了解线程创建、互斥体、条件变量。

对于需要支持多个平台的开发,需要开发者同时熟悉上述原理并编写多套适用不同平台的代码。

C++11 的线程库改变了这个现状,现在你只需要掌握 std::thread、std::mutex、std::condition_variable 少数几个线程同步对象即可,同时使用这些对象编写出来的代码也可以跨平台。示例如下:

c++11多线程

代码

  1. #include <thread>
  2. #include <mutex>
  3. #include <condition_variable>
  4. #include <list>
  5. #include <iostream>
  6. class Task
  7. {
  8. public:
  9. Task(int taskID)
  10. {
  11. this->taskID = taskID;
  12. }
  13. void doTask()
  14. {
  15. std::cout << "handle a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl;
  16. }
  17. private:
  18. int taskID;
  19. };
  20. std::mutex mymutex;
  21. std::list<Task*> tasks;
  22. std::condition_variable mycv;
  23. void* consumer_thread()
  24. {
  25. Task* pTask = NULL;
  26. while (true)
  27. {
  28. std::unique_lock<std::mutex> guard(mymutex);
  29. while (tasks.empty())
  30. {
  31. //如果获得了互斥锁,但是条件不合适的话,pthread_cond_wait会释放锁,不往下执行。
  32. //当发生变化后,条件合适,pthread_cond_wait将直接获得锁。
  33. mycv.wait(guard);
  34. }
  35. pTask = tasks.front();
  36. tasks.pop_front();
  37. if (pTask == NULL)
  38. continue;
  39. pTask->doTask();
  40. delete pTask;
  41. pTask = NULL;
  42. }
  43. return NULL;
  44. }
  45. void* producer_thread()
  46. {
  47. int taskID = 0;
  48. Task* pTask = NULL;
  49. while (true)
  50. {
  51. pTask = new Task(taskID);
  52. //使用括号减小guard锁的作用范围
  53. {
  54. std::lock_guard<std::mutex> guard(mymutex);
  55. tasks.push_back(pTask);
  56. std::cout << "produce a task, taskID: " << taskID << ", threadID: " << std::this_thread::get_id() << std::endl;
  57. }
  58. //释放信号量,通知消费者线程
  59. mycv.notify_one();
  60. taskID ++;
  61. //休眠1秒
  62. std::this_thread::sleep_for(std::chrono::seconds(1));
  63. }
  64. return NULL;
  65. }
  66. int main()
  67. {
  68. //创建5个消费者线程
  69. std::thread consumer1(consumer_thread);
  70. std::thread consumer2(consumer_thread);
  71. std::thread consumer3(consumer_thread);
  72. std::thread consumer4(consumer_thread);
  73. std::thread consumer5(consumer_thread);
  74. //创建一个生产者线程
  75. std::thread producer(producer_thread);
  76. producer.join();
  77. consumer1.join();
  78. consumer2.join();
  79. consumer3.join();
  80. consumer4.join();
  81. consumer5.join();
  82. return 0;
  83. }

C++并发编程实战

参考

发表评论

表情:
评论列表 (有 0 条评论,309人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C++11线初体验

    在C++11标准之前,使用C++编写多线程程序要么需要第三方的API如pthread,要么需要依赖运行平台提供的API,使用起来很不方便。而C++11提供了平台无关的语言级别的

    相关 C++11线异常

    一旦开始了线程,需要显示决定要等待线程函数完成或分离它自行完成。如果detach()线程不等待,你要确保通过线程访问的数据是有效的,直至该线程完成为止,例如线程函数持有局部变量

    相关 C++11线的使用

    C++11之前,C++语言没有对并发编程提供语言级别的支持,这使得我们在编写可移植的并发程序时,存在诸多不便。现在C++11增加了线程以及线程相关的类,很方便地支持了并发编程,

    相关 C++ 11 线--线管理

    关于c++11多线程,今天看到一篇讲的很好的文章,感谢原作者的分享,原文地址见 文章末尾 说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段。并行是