原创水平有限有误请指出
线程相比进程有着先天的数据共享的优势,如下图,线程共享了进程除栈区以外的所有内存区域如下图所示:
但是这种共享有时候也会带来问题,简单的考虑如下C++代码:
点击(此处)折叠或打开
- /*************************************************************************
- > File Name: error.cpp
- > Author: gaopeng QQ:22389860 all right reserved
- > Mail: gaopp_200217@163.com
- > Created Time: Mon 15 May 2017 12:01:33 AM CST
- ************************************************************************/
- #include<iostream>
- #include <pthread.h>
- #include <string.h>
- #define MAXOUT 1000000
- using namespace std;
- class testc
- {
- private:
- int a;
- public:
- testc()
- {
- a = 1;
- }
- testc& operator++()
- {
- int b = 0;
- b = a;
- a = b+1;
- return *this;
- }
- void prit()
- {
- cout<<a<<endl;
- }
- };
- testc test = test;
- void* testp(void* arg)
- {
- int i = MAXOUT;
- while(i--)
- {
- ++test;
- cout<<pthread_self() <<":";
- test.prit();
- }
- }
- int main(void)
- {
- pthread_t tid[3];
- int er;
- int i = 0;
- while(i<3)
- {
- if ((er = pthread_create(tid+i,NULL,testp,NULL) )!=0 )
- {
- strerror(er);
- return -1;
- }
- i++;
- }
- i = 0;
- while(i<3)
- {
- pthread_join(*(tid+i),NULL);
- i++;
- }
- cout<<"last numer: ";
- test.prit();
- }
实际上很简单我就是建立了一个全局变量,这个全局变量是全部线程共享,我开启了3个线程同时对里面的共享
数据进行更改,当
#define MAXOUT 100
的时候这个时候最后输出如下:
last numer: 300
显然没有问题,但是如果我将
#define MAXOUT 1000000
增大到100W的时候最后输出为:
last numer: 2999972
我们明显的感觉到数据不对了,少了一些
出现这种问题在于,多个线程对同一个共享资源进行访问,因为线程是CPU调度的单位,而
点击(此处)折叠或打开
- {
- int b = 0;
- b = a;
- a = b+1;
- return *this;
- }
假设这个时候a 为 1000:
这段代码并不是原子性的,假设线程1正在修改
b = a;
这个时候时间片用完,线程2也执行这段代码,因为
a = b+1;并没有执行,所以共享区域的数据还没有变化
线程2拿到的数据任然是1000,也执行如下代码
b = a;
a = b+1;
这个时候线程2完成了修改,a为1001
当下一次线程1拿到CPU时间片,再次从就绪状转到执行态,接着
执行
a = b+1;
因为线程1中的b还是1000,所以a再次修改为1001,实际这个时候
是1002才对,线程2的修改被覆盖,出现上面的问题,通过语言的描述
也许不太明白,下面的时序图会帮助理解:
遇到这种问题当然就引入了我们的线程间同步的方法,常用的
1、互斥锁、条件变量
2、读写锁
3、信号量
4、spinlock
他们保护的是一段临界区代码,所谓临界区就是可能出现对同一个共享资源进行修改的代码,比如我这里
点击(此处)折叠或打开
- {
- int b = 0;
- b = a;
- a = b+1;
- return *this;
- }
就是临界区代码
后面将对他们进行描述,这里我们简单实用静态互斥锁进行解决这个问题。
点击(此处)折叠或打开
- //原子操作 加锁
- pthread_mutex_lock(&mtx);
- ++test;
- pthread_mutex_unlock(&mtx);
- //原子操作 解锁
- cout<<pthread_self() <<":";
- test.prit()
实际上我们就是保护了操作符重载的testc& operator++()
临界区的选择应该尽量小,避免对多线程的并发性产生较大的性能影响
具体代码如下:
点击(此处)折叠或打开
- /*************************************************************************
- > File Name: error.cpp
- > Author: gaopeng QQ:22389860 all right reserved
- > Mail: gaopp_200217@163.com
- > Created Time: Mon 15 May 2017 12:01:33 AM CST
- ************************************************************************/
- #include<iostream>
- #include <pthread.h>
- #include <string.h>
- #define MAXOUT 1000000
- using namespace std;
- static pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;
- class testc
- {
- private:
- int a;
- public:
- testc()
- {
- a = 1;
- }
- testc& operator++()
- {
- int b = 0;
- b = a;
- a = b+1;
- return *this;
- }
- void prit()
- {
- cout<<a<<endl;
- }
- };
- testc test = test;
- void* testp(void* arg)
- {
- int i = MAXOUT;
- while(i--)
- {
- //原子操作 加锁
- pthread_mutex_lock(&mtx);
- ++test;
- pthread_mutex_unlock(&mtx);
- //原子操作 解锁
- cout<<pthread_self() <<":";
- test.prit();
- }
- }
- int main(void)
- {
- pthread_t tid[3];
- int er;
- int i = 0;
- while(i<3)
- {
- if ((er = pthread_create(tid+i,NULL,testp,NULL) )!=0 )
- {
- strerror(er);
- return -1;
- }
- i++;
- }
- i = 0;
- while(i<3)
- {
- pthread_join(*(tid+i),NULL);
- i++;
- }
- cout<<"last numer: ";
- test.prit();
- }
注意:一个简单类型的i++也不一定是一个原子操作,所以在涉及到并发修改共享变量的时候一定要使用
线程同步手段。
作者微信: