C++多线程/互斥锁/条件变量/信号量思维很重要;设计线程安全队列;1114按序打印;1115交替打印FooBar;1116打印零与奇偶数;1117H2O 生成1195交替打印字符串1226哲学家进餐
注:
//互斥量:unique_lock类封装,构造函数内加锁,析构函数自动解锁
mutex mut;
unique_lock<mutex> lock(mut);//范围语句结束才自动释放锁//条件变量:一般配合互斥量使用
condition_variable cond;
int cond0=1,cond1=0,cond2=0;//类似信号量PV思路
P操作:
for(...){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return cond0>0;});if(...){--cond0;...dosomething...cond.notify_all();//范围语句结束才通知所有睡眠线程}
}
V操作:
for(...){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return cond0>1;});if(...){++cond0;...dosomething... cond.notify_all();}
}//原子操作:一般只互斥访问一个全局变量的时候使用原子操作,如果获得互斥锁后的操作过多不建议使用
atomic<int> ato;
题目
设计线程安全队列:生产者消费者模型
//线程安全队列
template<typename T>
class threadSafeQueue{mutex mut;condition_variable cond;queue<T> q;int capacity;
public:threadSafeQueue()=default;~threadSafeQueue()=default;threadSafeQueue(int c):capacity(c){};//threadSafeQueue(threadSafeQueue &q)=delete;//threadSafeQueue(const threadSafeQueue &q)=delete;threadSafeQueue &operator=(const threadSafeQueue &q)=delete;void blockpush(const T &in){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->capacity>this->q.size();});q.push(in);cond.notify_all();}void blockpop(){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->q.size()>0;});q.pop();cond.notify_all();}T& blockfront(){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->q.size()>0;});cond.notify_all();return q.front();}bool empty(){unique_lock<mutex> lock(mut);return q.empty();}bool full(){unique_lock<mutex> lock(mut);return q.size()==capacity;}bool nonblockpush(const T &in){unique_lock<mutex> lock(mut);if(capacity==q.size())return false;q.push(in);cond.notify_all();return true;}bool nonblockpop(){unique_lock<mutex> lock(mut);if(q.empty())return false;q.pop();cond.notify_all();return true;}bool nonblockfront(T &ret){unique_lock<mutex> lock(mut);if(q.empty())return false;cond.notify_all();ret=q.front();return true;}
};
我们提供了一个类:
public class Foo {
public void one() { print("one"); }
public void two() { print("two"); }
public void three() { print("three"); }
}
三个不同的线程将会共用一个 Foo 实例。
线程 A 将会调用 one() 方法
线程 B 将会调用 two() 方法
线程 C 将会调用 three() 方法
请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。
示例 1:
输入: [1,2,3]
输出: "onetwothree"
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。
正确的输出是 "onetwothree"。
示例 2:
输入: [1,3,2]
输出: "onetwothree"
解释:
输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。
正确的输出是 "onetwothree"。
注意:
尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。
//自动变量:效率低
class Foo {atomic<int> ato;
public:Foo() {ato=0;}void first(function<void()> printFirst) { printFirst(); ++ato; }void second(function<void()> printSecond) {while(ato!=1);printSecond();++ato;}void third(function<void()> printThird) {while(ato!=2);printThird();}
};
/*互斥锁+条件变量:效率高
class Foo {mutex mut;condition_variable cond;int cond1,cond2;
public:Foo() {cond1=0,cond2=0;}void first(function<void()> printFirst) {unique_lock<mutex> lock(mut);printFirst();++cond1;cond.notify_all(); }void second(function<void()> printSecond) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond1==1;});printSecond();++cond2;cond.notify_all(); }void third(function<void()> printThird) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond2==1;});printThird();}
};*/
我们提供一个类:
class FooBar {
public void foo() {
for (int i = 0; i < n; i++) {
print("foo");
}
}
public void bar() {
for (int i = 0; i < n; i++) {
print("bar");
}
}
}
两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。
请设计修改程序,以确保 "foobar" 被输出 n 次。
示例 1:
输入: n = 1
输出: "foobar"
解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。
示例 2:
输入: n = 2
输出: "foobarfoobar"
解释: "foobar" 将被输出两次。
class FooBar {
private:int n;mutex mut;condition_variable cond;int cond1,cond2;
public:FooBar(int n) {this->n = n;cond1=0;cond2=1;}void foo(function<void()> printFoo) { for (int i = 0; i < n; i++) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond1+1==this->cond2;});// printFoo() outputs "foo". Do not change or remove this line.printFoo();++cond1;cond.notify_one();}}void bar(function<void()> printBar) {for (int i = 0; i < n; i++) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond1==this->cond2;});// printBar() outputs "bar". Do not change or remove this line.printBar();++cond2;cond.notify_one();}}
};
假设有这么一个类:
class ZeroEvenOdd {
public ZeroEvenOdd(int n) { ... } // 构造函数
public void zero(printNumber) { ... } // 仅打印出 0
public void even(printNumber) { ... } // 仅打印出 偶数
public void odd(printNumber) { ... } // 仅打印出 奇数
}
相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程:
线程 A 将调用 zero(),它只输出 0 。
线程 B 将调用 even(),它只输出偶数。
线程 C 将调用 odd(),它只输出奇数。
每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506... ,其中序列的长度必须为 2n。
示例 1:
输入:n = 2
输出:"0102"
说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 "0102"。
示例 2:
输入:n = 5
输出:"0102030405"
class ZeroEvenOdd {
private:int n;mutex mut;condition_variable cond;//信号量P V操作,资源个数bool cond0,cond1,cond2;
public:ZeroEvenOdd(int n) {this->n = n;cond0=true,cond1=false,cond2=false;}// printNumber(x) outputs "x", where x is an integer.void zero(function<void(int)> printNumber) {for(int i=1;i<=n;++i){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond0;});cond0=false;printNumber(0);if((i&1)==1)cond1=true;else cond2=true;cond.notify_all();} }void even(function<void(int)> printNumber) {for(int i=2;i<=n;i+=2){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond2;});cond2=false;printNumber(i);cond0=true;cond.notify_all();} }void odd(function<void(int)> printNumber) {for(int i=1;i<=n;i+=2){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond1;});cond1=false;printNumber(i);cond0=true;cond.notify_all();} }
};
现在有两种线程,氧 oxygen 和氢 hydrogen,你的目标是组织这两种线程来产生水分子。
存在一个屏障(barrier)使得每个线程必须等候直到一个完整水分子能够被产生出来。
氢和氧线程会被分别给予 releaseHydrogen 和 releaseOxygen 方法来允许它们突破屏障。
这些线程应该三三成组突破屏障并能立即组合产生一个水分子。
你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。
换句话说:
如果一个氧线程到达屏障时没有氢线程到达,它必须等候直到两个氢线程到达。
如果一个氢线程到达屏障时没有其它线程到达,它必须等候直到一个氧线程和另一个氢线程到达。
书写满足这些限制条件的氢、氧线程同步代码。
示例 1:
输入: "HOH"
输出: "HHO"
解释: "HOH" 和 "OHH" 依然都是有效解。
示例 2:
输入: "OOHHHH"
输出: "HHOHHO"
解释: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", "HOHOHH" 和 "OHHOHH" 依然都是有效解。
提示:
输入字符串的总长将会是 3n, 1 ≤ n ≤ 50;
输入字符串中的 “H” 总数将会是 2n 。
输入字符串中的 “O” 总数将会是 n 。
class H2O {mutex mut;condition_variable cond;int cntH,cntO;
public:H2O() {cntH=0,cntO=1;//2 2-1/2-0/0-1都可以}void hydrogen(function<void()> releaseHydrogen) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cntH;});--cntH;releaseHydrogen();if(cntH==0){++cntO;cond.notify_all();//}}void oxygen(function<void()> releaseOxygen) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cntO;});--cntO;releaseOxygen();cntH+=2;cond.notify_all();//1 容易漏掉}
};
编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:
如果这个数字可以被 3 整除,输出 "fizz"。
如果这个数字可以被 5 整除,输出 "buzz"。
如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。
例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。
假设有这么一个类:
class FizzBuzz {
public FizzBuzz(int n) { ... } // constructor
public void fizz(printFizz) { ... } // only output "fizz"
public void buzz(printBuzz) { ... } // only output "buzz"
public void fizzbuzz(printFizzBuzz) { ... } // only output "fizzbuzz"
public void number(printNumber) { ... } // only output the numbers
}
请你实现一个有四个线程的多线程版 FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:
线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。
线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。
线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。
线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。
class FizzBuzz {
private:int n;mutex mut;condition_variable cond;int condx,cond3,cond5,cond15;
public:FizzBuzz(int n) {this->n = n;condx=1,cond3=0,cond5=0,cond15=0;}// printFizz() outputs "fizz".void fizz(function<void()> printFizz) {for(int i=3;i<=n;i+=3){//1 +=3 否则超时unique_lock<mutex> lock(mut);if(i%15==0)continue;//2 否则超时cond.wait(lock,[this](){return this->cond3>0;}); --cond3,++condx;printFizz();cond.notify_all(); }}// printBuzz() outputs "buzz".void buzz(function<void()> printBuzz) {for(int i=5;i<=n;i+=5){//1 +=5unique_lock<mutex> lock(mut);if(i%15==0)continue;//2 cond.wait(lock,[this](){return this->cond5>0;}); --cond5,++condx;printBuzz();cond.notify_all(); }}// printFizzBuzz() outputs "fizzbuzz".void fizzbuzz(function<void()> printFizzBuzz) {for(int i=15;i<=n;i+=15){//1 +=15unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond15>0;}); --cond15,++condx;printFizzBuzz();cond.notify_all(); }}// printNumber(x) outputs "x", where x is an integer.void number(function<void(int)> printNumber) {for(int i=1;i<=n;++i){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->condx>0;});if(i%15==0){++cond15;--condx;cond.notify_all();}else if(i%3==0){++cond3;--condx;cond.notify_all();}else if(i%5==0){++cond5;--condx;cond.notify_all();}else printNumber(i);}}
};
/*超时
class FizzBuzz {
private:int n;mutex mut;condition_variable cond;int condx,cond3,cond5,cond15;
public:FizzBuzz(int n) {this->n = n;condx=1,cond3=0,cond5=0,cond15=0;}// printFizz() outputs "fizz".void fizz(function<void()> printFizz) {for(int i=1;i<=n;++i){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond3;}); if(i%3==0){--cond3,++condx;printFizz();cond.notify_all();} }}// printBuzz() outputs "buzz".void buzz(function<void()> printBuzz) {for(int i=1;i<=n;++i){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond5;}); if(i%5==0){--cond5,++condx;printBuzz();cond.notify_all();} }}// printFizzBuzz() outputs "fizzbuzz".void fizzbuzz(function<void()> printFizzBuzz) {for(int i=1;i<=n;++i){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->cond15;}); if(i%15==0){--cond15,++condx;printFizzBuzz();cond.notify_all();} }}// printNumber(x) outputs "x", where x is an integer.void number(function<void(int)> printNumber) {for(int i=1;i<=n;++i){unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->condx;});if(i%15==0)++cond15,--condx;else if(i%3==0)++cond3,--condx;else if(i%5==0)++cond5,--condx;else printNumber(i);cond.notify_all();}}
};*/
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):
philosopher 哲学家的编号。
pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
eat 表示吃面。
putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。
示例:
输入:n = 1
输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
解释:
n 表示每个哲学家需要进餐的次数。
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
output[i] = [a, b, c] (3个整数)
- a 哲学家编号。
- b 指定叉子:{1 : 左边, 2 : 右边}.
- c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。
提示:
1 <= n <= 60
class DiningPhilosophers {atomic<int>seat;
public:DiningPhilosophers() {seat=4;}void wantsToEat(int philosopher,function<void()> pickLeftFork,function<void()> pickRightFork,function<void()> eat,function<void()> putLeftFork,function<void()> putRightFork) {if(seat>0){--seat;pickLeftFork();pickRightFork();eat();putLeftFork();putRightFork();++seat;}}
};
/*
class DiningPhilosophers {mutex mut;condition_variable cond;int seat;
public:DiningPhilosophers() {seat=4;}void wantsToEat(int philosopher,function<void()> pickLeftFork,function<void()> pickRightFork,function<void()> eat,function<void()> putLeftFork,function<void()> putRightFork) {unique_lock<mutex> lock(mut);cond.wait(lock,[this](){return this->seat;});--seat;pickLeftFork();pickRightFork();eat();putLeftFork();putRightFork();++seat;cond.notify_one();}
};*/
C++多线程/互斥锁/条件变量/信号量思维很重要;设计线程安全队列;1114按序打印;1115交替打印FooBar;1116打印零与奇偶数;1117H2O 生成1195交替打印字符串1226哲学家进餐相关推荐
- 关于互斥锁,条件变量的内核源码解析
一.解决问题和适用范围 主要是用来等待一个条件,这个条件可能需要另一个线程来满足这个条件.这个和我们平常适用的pthread_mutex_lock的最大不同在于后者保护的一般是一个代码段(也就是关键区 ...
- 信号量 互斥锁 条件变量的区别
信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在哪里).而互斥锁是用在多线程多任务互斥的,一个线程占用了某 ...
- 信号量 互斥量 条件变量
原文:https://blog.csdn.net/qq_32646795/article/details/78221005 本文打算写一些和锁有关的东西,谈一谈我对锁的原理和实现的理解,主要包含以下方 ...
- linux C++ 多线程使用pthread_cond 条件变量
1. 背景 多线程中经常需要使用到锁(pthread_mutex_t)来完成多个线程之间的互斥操作. 但是互斥锁有一个明显到缺点: 只有两种状态,锁定和非锁定. 而条件变量则通过允许线程阻塞并等待另一 ...
- Linux内核中的同步原语:自旋锁,信号量,互斥锁,读写信号量,顺序锁
Linux内核中的同步原语 自旋锁,信号量,互斥锁,读写信号量,顺序锁 rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 1. Linux 内核中的同步原 ...
- java多线程互斥锁_浅谈Java多线程互斥锁
为了解决竞争条件带来的问题,我们可以对资源上锁.多个线程共同读写的资源称为共享资源,也叫临界资源.涉及操作临界资源的代码区域称为临界区(Critical Section).同一时刻,只能有一个线程进入 ...
- LeetCode 多线程 1116. 打印零与奇偶数
1116. 打印零与奇偶数 Ideas 有几个线程就用几个信号量,最先开始的信号量初始化为1,其它初始化为0,然后根据条件判断实现同步. 多线程的问题好多都是:锁自己,解锁别人. Code from ...
- 【C++】多线程互斥锁、条件变量
我们了解互斥量和条件变量之前,我们先来看一下为什么要有互斥量和条件变量这两个东西,了解为什么有这两东西之后,理解起来后面的东西就简单很多了!!! 先来看下面这段简单的代码: int g_num = 0 ...
- C++多线程 互斥锁 信号量 事件 临界区
一.互斥锁 1.先熟悉熟悉API 1,创建互斥锁,并反正一个句柄 HANDLE CreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全 ...
- C++多线程编程(2) 条件变量与原子操作
条件变量 条件变量是c++11 提供的另一种用于等待的同步机制,它能阻塞一个或者多个线程,直到收到另外一个线程发出的通知或者超时,才会唤醒当前阻塞的线程,条件变量需要和互斥量配合使用,C++11提供两 ...
最新文章
- Elasticearch 查询详解 (二)
- R中方差,协方差,相关系数
- WordPress调用自带jquery的方法 ,使 $ 生效
- ssh 配置文件中 maxsessions 与 MaxStartups
- 3-7 DNA序列(DNA Consensus String, ACM/ICPC Seoul 2006, UVa1368)
- git21天打卡day20-合并分支
- python创建对象教程_python源码学习 之 对象创建和对象的行为
- python桌面程序臃肿_Python自动化整理文件“大升级”,任意路径下文件,都给你整理的明明白白!...
- 基于遗传算法的TSP算法(附代码)
- 利用cmake来编译sim800c,sim868 app
- matlab求图像峰度与斜度,python中的图像偏斜和峰度
- 计算机系统结构变革在即?
- MindManager模板百度云下载分享教程
- 【python】实用tools
- 在线学习PS设计精讲精练记录(5)
- 两个瓶子水怎样一样多_大班科学领域数学活动 | 一样多的水(容积守恒)
- 计算机桌面没有打字,电脑输入法不见了怎么办
- 从程序员到项目经理(二十一):谁都需要成就感
- 2022-2028年中国医用级空气消毒机行业市场深度分析及前瞻研究报告
- 通过谷歌SEO网站建站提高网站质量,获取更多的客户