作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


题目地址:https://leetcode.com/problems/print-in-order/

题目描述

Suppose we have a class:

public class Foo {
public void first() { print("first"); }
public void second() { print("second"); }
public void third() { print("third"); }
}

The same instance of Foo will be passed to three different threads. Thread A will call first(), thread B will call second(), and thread C will call three(). Design a mechanism and modify the program to ensure that second() is executed after first(), and third() is executed after second().

Example 1:

Input: [1,2,3]
Output: "firstsecondthird"
Explanation: There are three threads being fired asynchronously. The input [1,2,3] means thread A calls first(), thread B calls second(), and thread C calls third(). "firstsecondthird" is the correct output.

Example 2:

Input: [1,3,2]
Output: "firstsecondthird"
Explanation: The input [1,3,2] means thread A calls first(), thread B calls third(), and thread C calls second(). "firstsecondthird" is the correct output.

Note:

We do not know how the threads will be scheduled in the operating system, even though the numbers in the input seems to imply the ordering. The input format you see is mainly to ensure our tests’ comprehensiveness.

题目大意

现在三个线程,每个线程分别调用三个函数中的一个。无论线程的产生和调用关系怎么样,最终输出的结果要求都是"firstsecondthird"。如何设计是三个函数。

解题方法

mutex锁

这个是Leetcode的新题型,也就是说并发类型,我觉得很实用,工作中能用到。

一般情况下,最简单的协调不同线程之间的调度关系,都可以使用mutex来做,本质是信号量。

std::mutex 的成员函数有四个:

  1. 构造函数,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 unlocked 状态的。
  2. lock(),调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:
  • (1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
  • (2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
  • (3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
  1. unlock(), 解锁,释放对互斥量的所有权。
  2. try_lock(),尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况,
  • (1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。
  • (2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。
  • (3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。

也就是说一个锁能控制两个线程的执行顺序。这个题中我们需要保持三个函数是按顺序执行的,则需要两个锁m1和m2。

在开始的时候,两个锁都锁起来。first()可以直接执行,second()等待m1释放之后执行,third()等待m2释放之后执行。first()结束之后释放m1,second()结束之后释放m2.因此三个的顺序都协调一致了。

C++代码如下:

class Foo {
private:
mutex m1, m2;
public:
Foo() {
m1.lock();
m2.lock();
} void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
m1.unlock();
} void second(function<void()> printSecond) {
m1.lock();
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
m1.unlock();
m2.unlock();
} void third(function<void()> printThird) {
m2.lock();
// printThird() outputs "third". Do not change or remove this line.
printThird();
m2.unlock();
}
}; void printFirst() {
cout << "first";
} void printSecond() {
cout << "second";
} void printThird() {
cout << "third";
}

promise/future

这也是C++11中的新特性,可以把promise和future当做是在不同线程之间传递值的方式。在某个线程中对promise中生产一个数据,可以在另外一个线程中从future中获取这个数据。

  • promise和future是绑定在一起的,可以调用promise::get_future()获取与其绑定的future。
  • future.wait()方法对当前的线程进行阻塞,等待与其绑定的promise调用set_value()方法。
  • future.get()方法对当前的线程进行阻塞,等待与其绑定的promise调用set_value()方法的返回值。

因此实现线程的同步的方法会很方便。C++代码如下:

class Foo {
private:
std::promise<void> p1;
std::promise<void> p2;
public:
Foo() {
} void first(function<void()> printFirst) {
// printFirst() outputs "first". Do not change or remove this line.
printFirst();
p1.set_value();
} void second(function<void()> printSecond) {
p1.get_future().wait();
// printSecond() outputs "second". Do not change or remove this line.
printSecond();
p2.set_value();
} void third(function<void()> printThird) {
p2.get_future().wait();
// printThird() outputs "third". Do not change or remove this line.
printThird();
}
}; void printFirst() {
cout << "first";
} void printSecond() {
cout << "second";
} void printThird() {
cout << "third";
}

参考资料:

  1. https://www.cnblogs.com/haippy/p/3237213.html
  2. http://www.cplusplus.com/reference/future/future/

日期

2019 年 7 月 14 日 —— 第一次做并发的题

【LeetCode】1114. Print in Order 解题报告(C++)的更多相关文章

  1. LeetCode 2 Add Two Sum 解题报告

    LeetCode 2 Add Two Sum 解题报告 LeetCode第二题 Add Two Sum 首先我们看题目要求: You are given two linked lists repres ...

  2. 【LeetCode】376. Wiggle Subsequence 解题报告(Python)

    [LeetCode]376. Wiggle Subsequence 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.c ...

  3. 【LeetCode】649. Dota2 Senate 解题报告(Python)

    [LeetCode]649. Dota2 Senate 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地 ...

  4. 【LeetCode】593. Valid Square 解题报告(Python)

    [LeetCode]593. Valid Square 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地 ...

  5. 【LeetCode】498. Diagonal Traverse 解题报告(Python)

    [LeetCode]498. Diagonal Traverse 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: htt ...

  6. 【LeetCode】386. Lexicographical Numbers 解题报告(Python)

    [LeetCode]386. Lexicographical Numbers 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博 ...

  7. 【LeetCode】722. Remove Comments 解题报告(Python)

    [LeetCode]722. Remove Comments 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/problems/remove-c ...

  8. 【LeetCode】86. Partition List 解题报告(Python)

    [LeetCode]86. Partition List 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http:// ...

  9. 【LeetCode】911. Online Election 解题报告(Python)

    [LeetCode]911. Online Election 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ ...

随机推荐

  1. R 语言实战-Part 5-2笔记

    R 语言实战(第二版) part 5-2 技能拓展 ----------第21章创建包-------------------------- #包是一套函数.文档和数据的合集,以一种标准的格式保存 #1 ...

  2. R语言与医学统计图形-【25】ggplot图形分面

    ggplot2绘图系统--图形分面 ggplot2的分面faceting,主要有三个函数: facet_grid facet_wrap facet_null (不分面) 1. facet_grid函数 ...

  3. EXCEL——排序函数RANK,6种花式使用技巧

    我们在实际工作中,常常把RANK函数用于对一列数据的基本排序,即从大到小的排序方法,那你还知道它的其他什么用法吗? 今天就给大家系统的分享下RANK函数的用法,分享的内容主要为以下这6种技巧. 1.升 ...

  4. PHP socket Workerman实用的php框架

    PHP socket Workerman是一款开源高性能异步PHP socket即时通讯框架. 非常好用的一款框架,可以支持在线聊天,长连接等 用法 官方 https://www.workerman. ...

  5. 24-Longest Palindromic Substring-Leetcode

    Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...

  6. gcc 的编译流程 和gdb的调试方法

    GCC的编译流程分为四个步骤: 预处理(Pre-Processing) 编译(Compiling) 汇编(Assembling) 链接(Linking) 可以看的出来文件大小 gdb 调试 gdb - ...

  7. C# CheckBoxList-DropDownList回显、筛选回显

    <asp:CheckBoxList ID="ddlType" runat="server" RepeatColumns="10" Re ...

  8. 如何将List集合中相同属性的对象合并

    在实际的业务处理中,我们经常会碰到需要合并同一个集合内相同属性对象的情况,比如,同一个用户短时间内下的订单,我们需要将各个订单的金额合并成一个总金额.那么用lambda表达式和HashMap怎么分别处 ...

  9. 【Linux】【Services】【nfs】nfs安装与配置

    1. 概念 1.1. NFS:Network File System,传统意义上,文件系统在内核中实现. 1.2. RPC:Remote Procedure Call protocol,远程过程调用, ...

  10. Apifox(2)快速上手apifox

    快速上手 使用场景 Apifox 是接口管理.开发.测试全流程集成工具,使用受众为整个研发技术团队,主要使用者为前端开发.后端开发和测试人员. 前端开发 接口文档管理 接口数据 Mock 接口调试 前 ...