深入学习c++--多线程编程(二)【当线程间需要共享非const资源】
1. 遇到的问题
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <cmath>
#include <vector>
#include <cstdlib>
using namespace std; class Counter
{
public:
void addCount() {
m_count++;
}
int count() const { return m_count; }
Counter() : m_count() { }
private:
int m_count;
}; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b);
c.addCount();
}
} int main()
{
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalValue, iter2, end](){
realWork(counter2, totalValue, iter2, end);
}); realWork(counter2, totalValue, vec.begin(), iter); b.join();
c.join();
cout << "total times use multithread: " << counter2.count() << " " << totalValue << endl; return ;
}

计算结果不一致!三个线程共享一份资源,有的加了有的没加。
2. 解决
2.1 法一:不共享变量
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <cmath>
#include <vector>
#include <cstdlib>
using namespace std; class Counter
{
public:
void addCount() {
m_count++;
}
int count() const { return m_count; }
Counter() : m_count() { }
private:
int m_count;
}; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b);
c.addCount();
}
} int main()
{
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
double totalC = ;
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalC, iter2, end](){
realWork(counter2, totalC, iter2, end);
}); double totalD = ;
realWork(counter2, totalD, vec.begin(), iter); b.join();
c.join();
cout << "total times use multithread: " << counter2.count() << " " << totalValue+totalC+totalD << endl; return ;
}

2.2 法二:原子操作变量类型(复杂,适合简单应用)
b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
count++: 写入寄存器,寄存器+1,写入内存
average()函数功能是如果Counter2不等于10000000,程序就不退出,如运行截图,由于共享变量counter2, 导致counter2总是无法等于10000000
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <string>
using namespace std; class Counter
{
public:
void addCount() {
m_count++;
}
int count() const { return m_count; }
Counter() : m_count() { }
private:
int m_count;
}; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b);
c.addCount();
}
} void printAll(int a, int b, int c)
{
cout << a << " " << b << " " << c << endl;
} void add(int a, int b, int& c)
{
c = a + b;
} void printString(const string& info, const string& info2)
{
cout << "hello " << info << " " << info2 << endl;
} void testThreadInit()
{
int a = ;
int b = ;
int c = ;
thread t([=](){
printAll(a, b, c);
});
t.join(); thread t2(printAll, a, b, c);
t2.join(); thread t3([=, &c](){
add(a, b, c);
});
t3.join();
cout << "after add: " << c << endl; // c是引用, 必须用 ref(c)
c = ;
thread t4(add, a, b, std::ref(c));
t4.join();
cout << "after add: " << c << endl; string abc("abc");
string def("def"); thread t5([&](){
printString(abc, def);
});
t5.join(); // 效率比引用低
thread t6(printString, abc, def);
t6.join(); // cref: 常引用
thread t7(printString, cref(abc), cref(def));
t7.join(); } bool average(Counter& c, int maxCount)
{
auto cnt = c.count();
if (cnt == maxCount) {
cout << " ok finished \n";
return true;
}
return false;
} int main()
{
testThreadInit(); // (1) 如果没有必要的话,线程间不要共享资源
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
thread printCount([&counter2](){
while (!average(counter2, )) {
}
});
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
double totalC = ;
//b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalC, iter2, end](){
realWork(counter2, totalC, iter2, end);
}); double totalD = ;
realWork(counter2, totalD, vec.begin(), iter);
b.join();
c.join(); auto realTotalCount = counter2.count(); totalValue += totalC + totalD;
cout << "total times use multithread: " << realTotalCount << " " << totalValue << endl; printCount.join();
// (2) return ;
}

解决:原子操作变量
只需要把int m_count; 改成 atomic<int> m_count; 即可
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <atomic>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <string>
using namespace std; class Counter
{
public:
void addCount() {
m_count++;
}
int count() const { return m_count; }
Counter() : m_count() { }
private:
// atomic_int m_count;
atomic<int> m_count; }; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b);
c.addCount();
}
} bool average(Counter& c, int maxCount)
{
auto cnt = c.count();
if (cnt == maxCount) {
cout << " ok finished \n";
return true;
}
return false;
} int main()
{
// (1) 如果没有必要的话,线程间不要共享资源
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
thread printCount([&counter2](){
while (!average(counter2, )) {
}
});
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
double totalC = ;
//b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalC, iter2, end](){
realWork(counter2, totalC, iter2, end);
}); double totalD = ;
realWork(counter2, totalD, vec.begin(), iter);
b.join();
c.join(); auto realTotalCount = counter2.count(); totalValue += totalC + totalD;
cout << "total times use multithread: " << realTotalCount << " " << totalValue << endl; printCount.join(); return ;
}

3. 新需求
两个变量,其中第一个变量变化,另一个还没来得及变化,另一个线程又变化了第一个变量

4. 解决:临界区--mutex
4.1 核心部分
void lockMutex() { m_mutex.lock(); }
void unlockMutex() { m_mutex.unlock(); }
c.lockMutex();
c.addCount();
c.addResource(); c.unlockMutex();
完整代码:(不是非常好的写法)
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <atomic>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <string>
#include <mutex>
using namespace std; class Counter
{
public:
void addCount() {
m_count++;
}
int count() const { return m_count; }
Counter() : m_count() { }
void addResource(int a) {
m_totalResource++;
}
int aveResource() {
if (m_count == )
return ;
return m_totalResource / m_count;
}
void lockMutex() { m_mutex.lock(); }
void unlockMutex() { m_mutex.unlock(); } private:
// atomic_int m_count;
atomic<int> m_count;
atomic<int> m_totalResource;
mutex m_mutex;
}; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b); c.lockMutex(); c.addCount();
c.addResource(); c.unlockMutex();
}
} bool average(Counter& c, int maxCount)
{
auto cnt = c.count(); c.lockMutex();
auto ave = c.aveResource();
if (ave != ) cout << "has bad thing happened\n";
c.unlockMutex(); if (cnt == maxCount) {
cout << " ok finished \n";
return true;
}
return false;
} int main()
{
// (1) 如果没有必要的话,线程间不要共享资源
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
thread printCount([&counter2](){
while (!average(counter2, )) {
}
});
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
double totalC = ;
//b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalC, iter2, end](){
realWork(counter2, totalC, iter2, end);
}); double totalD = ;
realWork(counter2, totalD, vec.begin(), iter);
b.join();
c.join(); auto realTotalCount = counter2.count(); totalValue += totalC + totalD;
cout << "total times use multithread: " << realTotalCount << " " << totalValue << endl; printCount.join(); return ;
}

注意:使用临界区,可能会发生死锁
4.2 将锁写到接口里
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <atomic>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <string>
#include <mutex>
using namespace std; class Counter
{
public:
Counter() : m_count(), m_totalResource() {} int count()
{
m_mutex.lock();
auto r = m_count;
m_mutex.unlock(); return r;
}
int aveResource() {
m_mutex.lock();
if (m_count == ) {
m_mutex.unlock();
return ;
}
auto r = m_totalResource / m_count;
m_mutex.unlock();
return r;
}
void addCoundAndResouce(int r)
{
m_mutex.lock();
addCount();
addResource(r);
m_mutex.unlock();
} private:
// atomic_int m_count;
void addResource(int a) {
m_totalResource++;
}
void addCount() {
m_count++;
}
int m_count;
int m_totalResource;
mutex m_mutex;
}; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b); c.addCoundAndResouce(); }
} bool average(Counter& c, int maxCount)
{
auto cnt = c.count(); auto ave = c.aveResource();
if (ave != ) cout << "has bad thing happened\n";
if (cnt == maxCount) {
cout << " ok finished \n";
return true;
}
return false;
} int main()
{
// (1) 如果没有必要的话,线程间不要共享资源
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
thread printCount([&counter2](){
while (!average(counter2, )) {
}
});
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
double totalC = ;
//b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalC, iter2, end](){
realWork(counter2, totalC, iter2, end);
}); double totalD = ;
realWork(counter2, totalD, vec.begin(), iter);
b.join();
c.join(); auto realTotalCount = counter2.count(); totalValue += totalC + totalD;
cout << "total times use multithread: " << realTotalCount << " " << totalValue << endl; printCount.join(); return ;
}
如果要把 count()设置成const
class Counter
{
public:
Counter() : m_count(), m_totalResource() {} int count() const
{
m_mutex.lock();
auto r = m_count;
m_mutex.unlock(); return r;
}
int aveResource() {
m_mutex.lock();
if (m_count == ) {
m_mutex.unlock();
return ;
}
auto r = m_totalResource / m_count;
m_mutex.unlock();
return r;
}
void addCoundAndResouce(int r)
{
m_mutex.lock();
addCount();
addResource(r);
m_mutex.unlock();
} private:
// atomic_int m_count;
void addResource(int a) {
m_totalResource++;
}
void addCount() {
m_count++;
}
int m_count;
int m_totalResource;
mutable mutex m_mutex;
};
将mutex设置成mutable类型
4.3 自定义lock类
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
#include <atomic>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <string>
#include <mutex>
using namespace std; template<typename T>
class Lock{
public:
Lock(T& mutex) : m_mutex(mutex) {
m_mutex.lock();
}
~Lock() { m_mutex.unlock(); }
private:
T& m_mutex;
}; class Counter
{
public:
Counter() : m_count(), m_totalResource() {} int count() const
{
Lock<mutex> lock(m_mutex);
return m_count;
}
int aveResource() {
Lock<mutex> lock(m_mutex);
if (m_count == ) {
return ;
}
return m_totalResource / m_count;
}
void addCoundAndResouce(int r)
{
Lock<mutex> lock(m_mutex);
addCount();
addResource(r);
m_mutex.unlock();
} private:
// atomic_int m_count;
void addResource(int a) {
m_totalResource++;
}
void addCount() {
m_count++;
}
int m_count;
int m_totalResource;
mutable mutex m_mutex;
}; int work(int a)
{
return a + a;
} template<class Iter>
void realWork(Counter& c, double &totalValue, Iter b, Iter e)
{
for (; b != e; ++b)
{
totalValue += work(*b); c.addCoundAndResouce(); }
} bool average(Counter& c, int maxCount)
{
auto cnt = c.count(); auto ave = c.aveResource();
if (ave != ) cout << "has bad thing happened\n";
if (cnt == maxCount) {
cout << " ok finished \n";
return true;
}
return false;
} int main()
{
// (1) 如果没有必要的话,线程间不要共享资源
unsigned n = std::thread::hardware_concurrency();
cout << n << " concurrent threads are support.\n"; vector<int> vec;
double totalValue = ;
for (int i = ; i < ; i++)
{
vec.push_back(rand() % );
}
Counter counter;
realWork(counter, totalValue, vec.begin(), vec.end());
cout << "total times: " << counter.count() << " " << totalValue << endl; totalValue = ;
Counter counter2;
thread printCount([&counter2](){
while (!average(counter2, )) {
}
});
auto iter = vec.begin() + (vec.size() / );
auto iter2 = vec.begin() + (vec.size() / * );
double totalC = ;
//b,c 线程共享了变量 counter2, 没有共享变量 totalValue,所以totalValue一样,counter2.count()不一样
thread b([&counter2, &totalValue, iter, iter2](){
realWork(counter2, totalValue, iter, iter2);
});
auto end = vec.end();
thread c([&counter2, &totalC, iter2, end](){
realWork(counter2, totalC, iter2, end);
}); double totalD = ;
realWork(counter2, totalD, vec.begin(), iter);
b.join();
c.join(); auto realTotalCount = counter2.count(); totalValue += totalC + totalD;
cout << "total times use multithread: " << realTotalCount << " " << totalValue << endl; printCount.join(); return ;
}
4.4 STL中的lock_guard
上述自定义lock换成lock_guard
lock_guard更灵活
class Counter
{
public:
Counter() : m_count(), m_totalResource() {} int count() const
{
lock_guard<mutex> lock(m_mutex);
return m_count;
}
int aveResource() {
lock_guard<mutex> lock(m_mutex);
if (m_count == ) {
return ;
}
return m_totalResource / m_count;
}
void addCoundAndResouce(int r)
{
lock_guard<mutex> lock(m_mutex);
addCount();
addResource(r);
m_mutex.unlock();
} private:
// atomic_int m_count;
void addResource(int a) {
m_totalResource++;
}
void addCount() {
m_count++;
}
int m_count;
int m_totalResource;
mutable mutex m_mutex;
};
4.5 死锁


alice往bob账户转钱,线程1被锁;同时bob也往alice账户转钱,线程2被锁 ===》产生死锁
4.6 lock_guard解决死锁方案
- lock(.....): 一口气将里面的临界体mutex都锁住
- lock_guard<mutex> locka(a.mutex, adopt_lock): 告诉已经锁住了,只需要析构的时候解锁一下

深入学习c++--多线程编程(二)【当线程间需要共享非const资源】的更多相关文章
- 多线程编程-- part 4 线程间的通信
线程间的相互作用 线程之间需要一些协调通信,来共同完成一件任务. Object类相关的方法:notify(),notifyAll(),wait().会被所有的类继承,这些方法是final不能被重写.他 ...
- java多线程编程(二创建线程)
1.概念 因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有 ...
- (Java多线程系列二)线程间同步
Java多线程间同步 1.什么是线程安全 通过一个案例了解线程安全 案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果. 先来看一个线程不安全的例子 class Sell ...
- UNIX环境编程学习笔记(27)——多线程编程(二):控制线程属性
lienhua342014-11-09 1 线程属性概括 POSIX 线程的主要属性包括 scope 属性.detach 属性.堆栈地址.堆栈大小.优先级.在头文件 pthread.h 中定义了结构体 ...
- APUE学习之多线程编程(三):线程属性、同步属性
一.线程属性 可以使用pthread_attr_t结构修改线程默认属性,并这些属性和创建的线程练习起来,可以使用pthread_att_init函数初始化pthread_attr_t结构,调 ...
- APUE学习之多线程编程(一):线程的创建和销毁
一.线程标识 和每个进程都有一个进程ID一样,每个线程也有一个线程ID,线程ID是以pthread_t数据类型来表示的,在Linux中,用无符号长整型表示pthread_t,Solaris ...
- Linux程序设计学习笔记----多线程编程线程同步机制之相互排斥量(锁)与读写锁
相互排斥锁通信机制 基本原理 相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下: ...
- Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)
一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...
- Linux系统编程@多线程编程(二)
线程的操作 线程标识 线程的ID表示数据类型:pthread_t (内核中的实现是unsigned long/unsigned int/指向pthread结构的指针(不可移植)几种类型) 1.对两个线 ...
随机推荐
- 怎么给win10进行分区?
新安装的win10系统的朋友,对于win10系统分区不满意应该如何是好呢?今天给大家介绍两种win10系统分区的方法,一个是windows自带分区管理软件来操作;另一个就是简单方便的分区助手来帮助您进 ...
- Java 中的函数式接口
java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子接口. Predicate -- 传入一个参数,返回一个bool结果, 方法为boolean test(T t) ...
- PHP高手干货分享:不能不看的50个细节!
1.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量, 单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的”函数”(译注:PHP手 ...
- java EL表达式中${param.name}详细
在浏览器地址输入,表示传入一个参数test,值为123 URL:http://localhost:8888/Test/index.jsp?test=123 <body> ${test} $ ...
- Spring Boot 2.x实战之定时任务调度
在后端开发中,有些场景是需要使用定时任务的,例如:定时同步一批数据.定时清理一些数据,在Spring Boot中提供了@Scheduled注解就提供了定时调度的功能,对于简单的.单机的调度方案是足够了 ...
- Django --- ajax结合sweetalert使用,分页器,bulk_create批量创建数据
目录 ajax结合sweetalert使用 bulk_create批量插入数据 分页器的使用 ajax结合sweetalert使用 ajax可以在不刷新页面的情况下与后端进行交互,在对数据进行操作的时 ...
- spark如何划分DAG视图
spark根据宽依赖进行DAG视图的划分. 1.窄依赖:每个父RDD的partition 最多被一个子RDD的 partition使用. 窄依赖分为两类:第一类是一对一的依赖关系,在Spark中用On ...
- 学习Spring-Data-Jpa(三)---关联关系常用注解
1.@JoinColumn: 用于指定 连接实体关联或元素集合的列. 属性 name: 外键列的名称,它所在的表取决于上下文. 如果连接是使用外键映射策略的一对一或多对一映射,则外键列位于源实体或可嵌 ...
- 浏览器中点击链接,跳转qq添加好友的实现方式
做android三年了,都不知道到底干了啥,现在好好研究应该来得及,哈哈哈,希望看到文章的人共勉,哈哈哈(新手写文章,大佬轻喷,呜呜呜~) 好了,这篇只是记录下,项目中遇到的坑(MMP测试),哈哈哈, ...
- pve 导入 ova
匆忙记录 Proxmox includes qm importdisk as command. Extract your ova: tar -xvf *.ova Create a new VM wit ...