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资源】的更多相关文章

  1. 多线程编程-- part 4 线程间的通信

    线程间的相互作用 线程之间需要一些协调通信,来共同完成一件任务. Object类相关的方法:notify(),notifyAll(),wait().会被所有的类继承,这些方法是final不能被重写.他 ...

  2. java多线程编程(二创建线程)

    1.概念           因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有 ...

  3. (Java多线程系列二)线程间同步

    Java多线程间同步 1.什么是线程安全 通过一个案例了解线程安全 案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果. 先来看一个线程不安全的例子 class Sell ...

  4. UNIX环境编程学习笔记(27)——多线程编程(二):控制线程属性

    lienhua342014-11-09 1 线程属性概括 POSIX 线程的主要属性包括 scope 属性.detach 属性.堆栈地址.堆栈大小.优先级.在头文件 pthread.h 中定义了结构体 ...

  5. APUE学习之多线程编程(三):线程属性、同步属性

    一.线程属性      可以使用pthread_attr_t结构修改线程默认属性,并这些属性和创建的线程练习起来,可以使用pthread_att_init函数初始化pthread_attr_t结构,调 ...

  6. APUE学习之多线程编程(一):线程的创建和销毁

    一.线程标识      和每个进程都有一个进程ID一样,每个线程也有一个线程ID,线程ID是以pthread_t数据类型来表示的,在Linux中,用无符号长整型表示pthread_t,Solaris ...

  7. Linux程序设计学习笔记----多线程编程线程同步机制之相互排斥量(锁)与读写锁

    相互排斥锁通信机制 基本原理 相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下: ...

  8. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  9. Linux系统编程@多线程编程(二)

    线程的操作 线程标识 线程的ID表示数据类型:pthread_t (内核中的实现是unsigned long/unsigned int/指向pthread结构的指针(不可移植)几种类型) 1.对两个线 ...

随机推荐

  1. redis都有哪些数据类型?分别在哪些场景下使用比较合适?

    (1)string 这是最基本的类型了,没啥可说的,就是普通的set和get,做简单的kv缓存 (2)hash 这个是类似map的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是这个对象 ...

  2. sql查询排序

        ORDER BY _column1, _column2; /* _column1升序,_column2升序 */   ORDER BY _column1, _column2 DESC; /* ...

  3. webpack起服务器报JavaScript heap out of memory

    配置如下: { "scripts": { "start": "node --max_old_space_size=4096 node_modules/ ...

  4. zookeeper先验知识(2PC+paxos)

    一.2PC两阶段提交: 在分布式事务中,每个机器节点只能够明确知道自己事务操作的结果,是成功还是失败,而无法获取其他分布式节点的操作结果,因此在事务操作需要跨多个分布式节点时,需要引入一个协调者统一调 ...

  5. 使用bufio包和函数式变成实现类似python生成器效果

    package main import ( "bufio" "fmt" "io" "strings" ) type in ...

  6. nginx反向代理 报错:Error during WebSocket handshake: Unexpected response code: 403

    遇到nginx报错:websocket wss failed: Error during WebSocket handshake: Unexpected response code: 403 serv ...

  7. asp.net实现大文件上传分片上传断点续传

    HTML部分 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="index.a ...

  8. 用SAM实现后缀排序

    因为本人几乎不会后缀数组,所以遇到这种SA的模板题也要拿SAM解决. 还是有一点思维难度的. 首先按照国际惯例,建反串的SAM. 然后对于这个反串,我们考虑两个前缀哪一个字典序小:因为是串是反的,所以 ...

  9. Chrome教程(一)NetWork面板分析网络请求

    官方文档:https://developers.google.com/web/tools/chrome-devtools/network/ 1.如何打开 无论是在Windows还是Mac,都可以使用( ...

  10. GoCN每日新闻(2019-09-25)

    GoCN每日新闻(2019-09-25) 1. Go module 再回顾 https://colobu.com/2019/09/23/review-go-module-again/2. 如何灵活地进 ...