一、(LINUX 线程同步) 引入

简介: 原创水平有限有误请指出 线程相比进程有着先天的数据共享的优势,如下图,线程共享了进程除栈区以外的所有内存区域如下图所示: 但是这种共享有时候也会带来问题,简单的考虑如下C++代码: 点击(此处)折叠或打开 ...
原创水平有限有误请指出

线程相比进程有着先天的数据共享的优势,如下图,线程共享了进程除栈区以外的所有内存区域如下图所示:


但是这种共享有时候也会带来问题,简单的考虑如下C++代码:

点击(此处)折叠或打开

  1. /*************************************************************************
  2.   > File Name: error.cpp
  3.   > Author: gaopeng QQ:22389860 all right reserved
  4.   > Mail: gaopp_200217@163.com
  5.   > Created Time: Mon 15 May 2017 12:01:33 AM CST
  6.  ************************************************************************/

  7. #include<iostream>
  8. #include <pthread.h>
  9. #include <string.h>
  10. #define MAXOUT 1000000
  11. using namespace std;



  12. class testc
  13. {
  14.         private:
  15.                 int a;
  16.         public:
  17.                 testc()
  18.                 {
  19.                         a = 1;
  20.                 }
  21.                 testc& operator++()
  22.                 {
  23.                         int b = 0;
  24.                         b = a;
  25.                         a = b+1;
  26.                         return *this;
  27.                 }
  28.                 void prit()
  29.                 {
  30.                         cout<<a<<endl;
  31.                 }
  32. };


  33. testc test = test;


  34. void* testp(void* arg)
  35. {
  36.         int i = MAXOUT;
  37.         while(i--)
  38.         {
  39.                 ++test;
  40.                 cout<<pthread_self() <<":";
  41.                 test.prit();
  42.         }
  43. }




  44. int main(void)
  45. {
  46.         pthread_t tid[3];
  47.         int er;
  48.         int i = 0;

  49.         while(i<3)
  50.         {

  51.                 if ((er = pthread_create(tid+i,NULL,testp,NULL) )!=0 )
  52.                 {
  53.                         strerror(er);
  54.                         return -1;
  55.                 }
  56.                 i++;
  57.         }

  58.         i = 0;

  59.         while(i<3)
  60.         {
  61.                 pthread_join(*(tid+i),NULL);
  62.                 i++;
  63.         }
  64.         cout<<"last numer: ";
  65.         test.prit();
  66. }



实际上很简单我就是建立了一个全局变量,这个全局变量是全部线程共享,我开启了3个线程同时对里面的共享
数据进行更改,当
#define MAXOUT 100 
的时候这个时候最后输出如下:
last numer: 300
显然没有问题,但是如果我将
#define MAXOUT 1000000
增大到100W的时候最后输出为:
last numer: 2999972
我们明显的感觉到数据不对了,少了一些
出现这种问题在于,多个线程对同一个共享资源进行访问,因为线程是CPU调度的单位,而

点击(此处)折叠或打开

  1. {
  2.                         int b = 0;
  3.                         b = a;
  4.                         a = b+1;
  5.                         return *this;
  6.                 }


假设这个时候a 为 1000:
这段代码并不是原子性的,假设线程1正在修改
=  a ;
这个时候时间片用完,线程2也执行这段代码,因为
=  b + 1 ;并没有执行,所以共享区域的数据还没有变化
线程2拿到的数据任然是1000,也执行如下代码
=  a ;
=  b + 1 ;
这个时候线程2完成了修改,a为1001
当下一次线程1拿到CPU时间片,再次从就绪状转到执行态,接着
执行
=  b + 1 ;
因为线程1中的b还是1000,所以a再次修改为1001,实际这个时候
是1002才对,
线程2的修改被覆盖,出现上面的问题,通过语言的描述
也许不太明白,下面的时序图会帮助理解:



遇到这种问题当然就引入了我们的线程间同步的方法,常用的
1、互斥锁、条件变量
2、读写锁
3、信号量
4、spinlock
他们保护的是一段临界区代码,所谓临界区就是可能出现对同一个共享资源进行修改的代码,比如我这里

点击(此处)折叠或打开

  1. {
  2.                         int b = 0;
  3.                         b = a;
  4.                         a = b+1;
  5.                         return *this;
  6.                 }


就是临界区代码
后面将对他们进行描述,这里我们简单实用静态互斥锁进行解决这个问题。

点击(此处)折叠或打开

  1. //原子操作 加锁
  2.                 pthread_mutex_lock(&mtx);
  3.                 ++test;
  4.                 pthread_mutex_unlock(&mtx);
  5.                 //原子操作 解锁
  6.                 cout<<pthread_self() <<":";
  7.                 test.prit()
实际上我们就是保护了操作符重载的 testc &  operator + + ( )
临界区的选择应该尽量小,避免对多线程的并发性产生较大的性能影响


具体代码如下:

点击(此处)折叠或打开

  1. /*************************************************************************
  2.   > File Name: error.cpp
  3.   > Author: gaopeng QQ:22389860 all right reserved
  4.   > Mail: gaopp_200217@163.com
  5.   > Created Time: Mon 15 May 2017 12:01:33 AM CST
  6.  ************************************************************************/

  7. #include<iostream>
  8. #include <pthread.h>
  9. #include <string.h>
  10. #define MAXOUT 1000000
  11. using namespace std;

  12. static pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;


  13. class testc
  14. {
  15.         private:
  16.                 int a;
  17.         public:
  18.                 testc()
  19.                 {
  20.                         a = 1;
  21.                 }
  22.                 testc& operator++()
  23.                 {
  24.                         int b = 0;
  25.                         b = a;
  26.                         a = b+1;
  27.                         return *this;

  28.                 }
  29.                 void prit()
  30.                 {
  31.                         cout<<a<<endl;
  32.                 }
  33. };


  34. testc test = test;


  35. void* testp(void* arg)
  36. {
  37.         int i = MAXOUT;

  38.         while(i--)
  39.         {
  40.                 //原子操作 加锁
  41.                 pthread_mutex_lock(&mtx);
  42.                 ++test;
  43.                 pthread_mutex_unlock(&mtx);
  44.                 //原子操作 解锁
  45.                 cout<<pthread_self() <<":";
  46.                 test.prit();
  47.         }
  48. }




  49. int main(void)
  50. {
  51.         pthread_t tid[3];
  52.         int er;
  53.         int i = 0;

  54.         while(i<3)
  55.         {

  56.                 if ((er = pthread_create(tid+i,NULL,testp,NULL) )!=0 )
  57.                 {
  58.                         strerror(er);
  59.                         return -1;
  60.                 }
  61.                 i++;
  62.         }

  63.         i = 0;

  64.         while(i<3)
  65.         {
  66.                 pthread_join(*(tid+i),NULL);
  67.                 i++;
  68.         }
  69.         cout<<"last numer: ";
  70.         test.prit();
  71. }


注意:一个简单类型的i++也不一定是一个原子操作,所以在涉及到并发修改共享变量的时候一定要使用
线程同步手段。



作者微信:

               
相关文章
|
6月前
|
消息中间件 存储 缓存
【嵌入式软件工程师面经】Linux系统编程(线程进程)
【嵌入式软件工程师面经】Linux系统编程(线程进程)
128 1
|
4月前
|
算法 Unix Linux
linux线程调度策略
linux线程调度策略
91 0
|
2月前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
33 0
Linux C/C++之线程基础
|
2月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
4月前
|
存储 设计模式 NoSQL
Linux线程详解
Linux线程详解
|
4月前
|
缓存 Linux C语言
Linux线程是如何创建的
【8月更文挑战第5天】线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。
|
4月前
|
负载均衡 Linux 调度
在Linux中,进程和线程有何作用?
在Linux中,进程和线程有何作用?
|
4月前
|
缓存 Linux C语言
Linux中线程是如何创建的
【8月更文挑战第15天】线程并非纯内核机制,由内核态与用户态共同实现。
|
6月前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现
|
6月前
|
Linux API
Linux线程总结---线程的创建、退出、取消、回收、分离属性
Linux线程总结---线程的创建、退出、取消、回收、分离属性