• linux生产者消费者模型


    今天是一个与互斥锁和条件变量有关的一个模型,生产者消费者模型,为什么要用这个模型呢?其实这个模型我个人感觉的有点就是提高了效率,在多线程的情况下,提高了非常明显。并且解耦了生产者和消费者的关系。下面是一个这个模型的基本思路:

    这个模型充分说明了两者的关系,就是一个共用的资源,一个放,一个拿,且有三种关系,两种角色,一个交易场所。取数据的我们可以看成是消费者,放数据的是生产者,交易场所就是这个共用资源,而这个关系我们应该怎样理解呢?就是如果有多个生产者和消费者,那么我们就要出现一个串行关系取数据或是放数据,不然就会乱,所以会很明显的是消费者之间是互斥关系,生产者之间也是互斥,而生产者与消费者是互斥与同步的关系。,所以弄清楚了基本的模型,我们就来看看如何实现。.

    注:这个交易场所也就是共用资源可以不是队列,这里只是打个比方,为了更好的理解。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "mtx.hpp"
    7. #include "model.hpp"
    8. #include
    9. using namespace std;
    10. //创建线程数量
    11. #define NUMPTHREAD_T 6
    12. //函数指针
    13. typedef void (*func)(int);
    14. //函数声明
    15. void product(int number);
    16. void custcom(int number);
    17. proCust<int> mod;
    18. //判断是生产者还是消费者
    19. struct Pro_or_cust
    20. {
    21. int _number;
    22. string _name;
    23. func _product = product;
    24. func _custcom = custcom;
    25. Pro_or_cust(int number)
    26. : _number(number)
    27. {
    28. }
    29. };
    30. void product(int number)
    31. {
    32. while (true)
    33. {
    34. //同步所有线程
    35. sleep(3);
    36. //随机生成数,项目中一般是做其他任务
    37. int val = rand() % 100000;
    38. cout << "我是生产者" << pthread_self() << " " << number << endl;
    39. mod.push(val, number);
    40. sleep(1);
    41. }
    42. }
    43. void custcom(int number)
    44. {
    45. while (true)
    46. {
    47. cout << "我是消费者" << pthread_self() << " " << number << endl;
    48. int ret = mod.pop(number);
    49. //打印获得数据,一般也可以处理获取的数据
    50. cout << "我消费的数据" << ret << endl;
    51. sleep(1);
    52. }
    53. }
    54. void *enter(void *args)
    55. {
    56. Pro_or_cust *p = (Pro_or_cust *)args;
    57. //判断是生产者还是消费者
    58. if (strcmp(p->_name.c_str(), "custcom") == 0)
    59. p->_custcom(p->_number);
    60. else
    61. p->_product(p->_number);
    62. delete p;
    63. return nullptr;
    64. }
    65. int main()
    66. {
    67. srand((unsigned int)time(nullptr));
    68. pthread_t tid[NUMPTHREAD_T];
    69. for (int i = 0; i < NUMPTHREAD_T; i++)
    70. {
    71. Pro_or_cust *str = new Pro_or_cust(i + 1);
    72. if (i % 2 == 0)
    73. str->_name = "custcom";
    74. else
    75. str->_name = "product";
    76. pthread_create(tid + i, nullptr, enter, str);
    77. // sleep(1);
    78. }
    79. for (int i = 0; i < NUMPTHREAD_T; i++)
    80. {
    81. pthread_join(tid[i], nullptr);
    82. }
    83. cout << "回收资源成功" << endl;
    84. return 0;
    85. }
    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include "mtx.hpp"
    8. #define NUMSIZE 10
    9. using namespace std;
    10. template <class T>
    11. class proCust
    12. {
    13. public:
    14. proCust(size_t size = NUMSIZE)
    15. : _size(size)
    16. {
    17. pthread_mutex_init(&_mtx, nullptr);
    18. pthread_cond_init(&_full, nullptr);
    19. pthread_cond_init(&_empty, nullptr);
    20. }
    21. void push(const T &x, int number)
    22. {
    23. Rmtx s(_mtx);
    24. cout<<"我抢到锁了"<" " <<&_mtx<
    25. sleep(1);
    26. while (_size == _q.size())
    27. {
    28. pthread_cond_signal(&_empty);
    29. pthread_cond_wait(&_full, &_mtx);
    30. }
    31. _q.push(x);
    32. cout << "我是生产者:" << number << "号"
    33. << " "
    34. << "我的tid: " << pthread_self() << endl;
    35. }
    36. T pop(int number)
    37. {
    38. Rmtx s(_mtx);
    39. cout<<"我抢到锁了"<" " <<&_mtx<
    40. sleep(1);
    41. while (_q.size() == 0)
    42. {
    43. pthread_cond_signal(&_full);
    44. pthread_cond_wait(&_empty, &_mtx);
    45. }
    46. T x = _q.front();
    47. _q.pop();
    48. cout << "我是消费者:" << number << "号"
    49. << " "
    50. << "我的tid: " << pthread_self() << endl;
    51. return x;
    52. }
    53. ~proCust()
    54. {
    55. pthread_mutex_destroy(&_mtx);
    56. pthread_cond_destroy(&_full);
    57. pthread_cond_destroy(&_empty);
    58. }
    59. private:
    60. size_t _size;
    61. queue _q;
    62. pthread_cond_t _full;
    63. pthread_cond_t _empty;
    64. pthread_mutex_t _mtx;
    65. };
    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. //RAII风格的加锁方式
    6. struct Rmtx
    7. {
    8. public:
    9. Rmtx(pthread_mutex_t &mtx)
    10. : _mtx(mtx)
    11. {
    12. pthread_mutex_lock(&_mtx);
    13. std::cout << "已上锁" << std::endl;
    14. }
    15. ~Rmtx()
    16. {
    17. pthread_mutex_unlock(&_mtx);
    18. std::cout << "已解锁" << std::endl;
    19. }
    20. private:
    21. pthread_mutex_t &_mtx;
    22. };

    首先是两个头文件,一个test.cc文件。

    这个模型本身很简单,但是实现起来,个人认为还是要有不少注意的点,而且不好找出错误。

    首先给大家说说实现这个模型我个人认为要注意的点。

    1.如果有小伙伴想给新线程传编号,就是第几个创建的线程,这里一定要注意。那就是因为pthread_create这个函数本身不会有延迟,但是奈何这个函数中,是创建一个新线程,也就是说这个函数内部就会分开,一个是新线程,一个是主线程,而主线程的速度要比新线程快,所以有可能就是主线程运行到第二次循环了,新线程可能才创建好,所以这里有时候会达不到自己的预期,切代码正确,就是检查不出错误原因。

    2.其次就是这个RAII的风格方式加锁,不知道大家写的时候有没有遇见过,就是把锁的初始化写到了构造函数(上面的代码是写到Rmtx中构造函数),其实这样写是错的,这样的话类似于加不上锁,我也测试了很长时间才发现,因为这样的话每个线程都会创建一个这个对象,所以会把这个锁初始化好多次,造成类似于没有加锁的那种情况,就是多线程共同访问临界资源,这个一定要注意。

    我上网查了下类似于同步多次初始化的结果,但是没查到,有的也没看懂,我个人认为可能是破坏了锁,所以导致错误。

    多线程部分出现错误还是比较难调试的,所以一定要小心。

    希望大家支持!!!

  • 相关阅读:
    Python学习第1天-安装Python
    presto和hive中grouping sets的格式不一致问题
    Adobe Acrobat Pro DC 2023:提升工作效率,激发创意灵感 mac/win版
    (十二)Flask重点之session
    gin源码实战 day2
    计算机毕业设计ssm社区爱心活动网站be83l系统+程序+源码+lw+远程部署
    iOS开发:多线程之理解任务和队列
    2022届秋招Java岗高频面试题盘点,老司机也未必全会,真的太卷了
    1-十六烷基-3-三乙氧基丙基硅烷咪唑溴盐离子液体([HDTIm]Br)修饰磁性纳米颗粒(MNPs)|[HDTIm]Br-MNPs
    <三>Qt斗地主游戏开发:主界面初始化显示
  • 原文地址:https://blog.csdn.net/huichaochao/article/details/134300212