Code Monkey home page Code Monkey logo

cpp-concurrency-in-action-2ed-2019's Introduction

C++ Concurrency In Action

SECOND EDITION

  • 作者:Anthony Williams
  • 译者:陈晓伟

翻译是译者用自己的**,换一种语言,对原作者想法的重新阐释。鉴于我的学识所限,误解和错译在所难免。如果你能买到本书的原版,且有能力阅读英文,请直接去读原文。因为与之相较,我的译文可能根本不值得一读。

— 云风,程序员修炼之道第2版译者

本书概述

作为对《C++ Concurrency in Action - SECOND EDITION》的中文翻译。

第二版根据C++14和C++17标准进行更新和修订,涵盖了所有标准中最新的改动!本次修订版所要解答的问题是,如何用C++17写出优雅且健壮的多线程应用,并告诉你所有的细节!

技术方面

当需要应用有足够快的运行速度的时候,您应该选择C++。设计良好的C++并发程序将会充分利用资源,并运行的更快。C++17可以使用多线程或多处理器,使得图像处理、机器学习等性能敏感型任务更加快速的完成。本书特别为工业级C++并发解锁了相应的特性、模式和最佳实践方式。

关于本书

C++ Concurrency in Action, Second Edition可以作为C++在编写优雅多线程应用方面的权威指南。并更新了C++17的相关内容,其详细描述了并行开发的各个方面,从启动新线程到设计复杂的多线程算法和数据结构。每一章中并发大师Anthony Williams都为你提供了示例和一些练习,也包括一些独到的见解,这对于开发经验丰富人员来说可能是非常有兴趣的。

内容概述

  • 对C++17特性全面覆盖
  • 启动和管理线程
  • 同步并发操作
  • 设计并发代码
  • 调试多线程应用

读者须知

本书为C和C++开发人员所编写。无需任何并发经验,在看本书的时候,大家都在同一“起跑线”上。

本书相关

cpp-concurrency-in-action-2ed-2019's People

Contributors

ianzone avatar lingr7 avatar maplefu avatar mickeyouyou avatar wanghenshui avatar xiaoweichen avatar yanggggman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cpp-concurrency-in-action-2ed-2019's Issues

9.2.5代码错误

template<typename T>
void interruptible_wait(std::future<T>& uf)
{
  while(!this_thread_interrupt_flag.is_set())
  {
    //future.wait_for没有接收lock类型参数的方法
    //if(uf.wait_for(lk,std::chrono::milliseconds(1)==
    //   std::future_status::ready)
    if(uf.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) {
      break;
    }
  }
  interruption_point();
}

一个错别字?

在关于本书-软件需求:
"我的公司Software Solutions Ltd,销售C++11标准线程库的完整实现,其可以使用在一些 的编译器上"

2.3 第一段难以读懂

原文:

Suppose you want to write a function that creates a thread to run in the background but passes back ownership of the new thread to the calling function rather than waiting for it to complete, or maybe you want to do the reverse: create a thread and pass ownership in to some function that should wait for it to complete. In either case, you need to transfer ownership from one place to another.

本文:

假设要写一个在后台启动线程的函数,并想通过新线程返回的所有权去调用这个函数,而不 是等待线程结束再去调用;或完全与之相反的想法:创建一个线程,并在函数中转移所有 权,都必须要等待线程结束。所以,新线程的所有权都需要转移。

我觉得:

假定你编写了一个创建了新线程的函数,该函数需要将新线程的所有权返回给调用者(而非等待新线程完成);或者相反的,你创建了一个线程并希望将它的所有权转移给接下来调用的函数的内部(这些函数将会等待线程完成)。无论哪种情况,你都需要将线程的所有权从一个调用者转移到另一个调用者。

我觉得本文这个翻译太难懂了,但是英文原版一看就知道讲的什么情况了,建议修改下。

源代码错误

9.1节的代码清单 9.3中的第20行
原代码:
futures[i]=pool.submit(accumulate_block<Iterator,T>());
应改为:
futures[i]=pool.submit( [=] (){
return accumulate_block<Iterator, T>()(block_start, block_end);
});

关于先前的2.3,翻译有误处的想法

看到译者在改成了

std::thread中对移动语义的支持,也适用于使用std::thread的移动敏感(move-aware)容器(比如,std::vector<>)。了解这些后,就可以量产了一些线程,并且等待它们结束,代码如下所示。

我其实想强调一点,就是准确。

std::thread支持移动语义,这使得使用std::thread的容器对象(std::vector<>)在使用std::thread作为容器类型时,同样是移动敏感的。

在C++11中提出了右值引用、移动语义,在STL中完美实现了这一点,在利用移动拷贝构造函数时,移动赋值构造函数时,都能实现移动语义。此时STL能够利用std::thread内部实现的移动语义去实现这样的移动构造函数,减少相应的开销。

原文中的核心在于:了解这一点,就可以利用这一条性质去利用容器量产线程(通过容器的元素构造形式去量产线程)

代码中采用了emplace_back去生成相应的线程,实际上这里的emplace_back利用的是“完美转发”,将所有的参数直接转发到std::thread的构造函数去,而完美转发的实现依赖于移动语义。

这意味着一点,std::thread不能拷贝形成。

我非常想拿我前段时间写的一些代码做例子去阐述这一点。否则这令我有点头疼,下面是我写的一个小例子与相关的结果

image

image

image

image

这样的一个例子阐述了移动语义在类似情况下的应用,这本书在阐述std::thread的不可拷贝之类的属性的时候,强调的就是std::thread通过移动语义转移所有权,这和unique_ptr类似。

希望我的解释能帮到你,(主要是翻译让我感到头疼,上一次也是我第一次提issue,如有不当,请指出。)

关于listing 翻译的建议

listing在文中部分区域,被翻译为清单。

在列出代码时,称作”代码“。

两处的翻译不一致是有一点点令人难受的。

7.2.1有疑问

原文中提到

最简单的栈就是链表, head指针指向第一个节点(可能是下一个被索引到的节点), 并且每个节点依次指向下一个节点。
在这样的情况下,添加一个节点相对来说很简单:

  1. 创建一个新节点
  2. 将新节点的next指针指向当前的head节点
  3. 让head节点指向新节点

举个栗子,head->A->B,新添加一个节点C,最后不应该是head->C->A->B吗?

const node* C = new node(c);
C->next = head->next; //C->next指向A->B
head->next = C; //head->next指向C->A->B

不知道是不是我理解的有问题?

5.3.3节的翻译错误

不好意思,提一个翻译失误.不清楚如何反馈,所以只能在这里提出来.
5.3.3节,清单5.8下方有个翻译错了,
原文是:The store to x① happens before the store to y②
译文是:存储x①先行于加载y②

提一个意见
原文:The store to x① happens before the store to y② because they’re in the same thread
译文:因为这两个操作是由同一个线程完成的,所以存储x①先行于加载y②
改为,存储x先加载于存储y,因为他们在同一个线程中,更好理解.
我体会了好久,又看了原文,才知道就是这个意思,译文缺少主语,需要靠猜测判断.虽然是这样的,但明确些更好

7.1.3 无等待数据结构

翻译

由于会和别的线程产生冲突,所以算法可以进行无数次尝试,因此并不是无等待的

原文

Algorithms that can involve an unbounded number of retries because of clashes with other threads are thus not wait-free

建议

如果因为跟其他线程冲突而导致算法会进行无数次重试,那么就不是无等待的

翻译有误

2.3 转移所有权中
代码2.7下面的一段话。

std::thread对象的容器,如果这个容器是移动敏感的(比如,标准中的std::vector<>),那么移动操作同样适用于这些容器。了解这些后,就可以写出类似代码2.7中的代码,代码量产了一些线程,并且等待它们结束。

原文

The move support in std::thread also allows for containers of std::thread objects, if those containers are move-aware (like the updated std::vector<>).This means that you can write code like that in the following listing, which spawns a numbers of threads and then waits for them to finish.

建议改成

在std::thread中对移动语义的支持同样适用于使用std::thread的容器对象,前提是那些容器同样是移动敏感的(move-aware),例如std::vector<>。了解这些后,就可以写出类似代码2.8中的代码。2.8通过容器量产了一些线程,并等待它们结束。

原因:原文中强调的是2.8的代码,而不是2.7的代码,这里具有误导性。

7.2.1节翻译问题

Unfortunately, here you don’t have that luxury; you can only safely copy the data once you know you’re the only thread returning the node, which means the node has already been removed from the queue.
关于pop为什么不使用引用的论述。
您的翻译是
不幸的是,不能这样做,只能在单一线程对值进行返回时,才进行拷贝以确保拷贝操作的安全性,这就意味着在拷贝结束后这个节点就会删除。
但事实上这段话与拷贝结束没有关系。
提供一段参考:
不幸的是,在这里你不能这样做。当线程返回这个节点时你才能安全地进行拷贝,但此刻这个节点已经从队列中被删除了。

第五章一些问题的商讨

首先感谢作者翻译。但是个人感觉第五章的 load, store, 以及各种内存序,synchronize-with, happens-before 这些术语,即使在国内社区,讨论的时候更多也是不翻译的。能否考虑将以上部分改回英文呢?
作者如果考虑更改这一块的话,我可以负责对 5.2 和 5.3 节这部分翻译进行一定修改。

诸多的翻译不准确地方

刚看到第二章,发现翻译中诸多地方要么翻译不准确,要么翻译的没有抓住要点,要么直接省略了个别语句,有时候会给阅读者带来误导,乍一读感觉也通顺,仔细品味发现其实不然,再去看英文原版,其实原版讲的很通俗易懂,开始还以为是原版作者写的比较难懂

希望作者有空再厘清一下,翻译不合适的地方比较多,无法一一列举了

采用两端对齐

提个小建议,可以使用两端对齐格式化文本后再导出pdf

3.3.1代码3.11前的翻译建议

image

3.3.1 代码3.11前
一段话

转为多线程代码时,只有①处需要保护,这样共享数据对于并发访问就是安全的。但是下面天真的转换会使得线程资源产生不必要的序列化,为了确定数据源已经初始化,每个线程必须等待互斥量。

原文

If the shared resource itself is safe for concurrent access, the only part that needs protecting when converting this to multithreaed code is initialization①, but a naive translation such as that in the following listing can cause unnecessary serialization of threads using the resource. This is because each thread must wait on the mutex in orther to check whether the resource has already been initialized.

建议改成

如果共享资源对于并发访问是安全的,那么唯一需要保护的地方就是①处(此处指上段代码中的①)。否则,按照3.11代码所示,所有的线程在进入foo()时会依次按顺序访问以避免同时访问,这显然是不必要的。这是因为每一个线程必须等待获取mutex才能确认资源是否被初始化。

原因
原文中的意思强调的是“当仅仅对共享资源需要初始化时,才需要提供锁避免重复初始化”。而这一大段一大段的介绍都是为了强调这一点,这些翻译让我觉得非常晦涩难懂,并且没有参照原文的逻辑顺序。
英文逻辑性是非常强的,同时,翻译时要尽可能完整,不偏离本身英文逻辑的含义,非常建议在翻译的时候,1.完整性,2.理解原文的含义。

理解误差:2.1.1

原文:
In this case, the supplied function object is copied into the storage belonging to the
newly created thread of execution and invoked from there. It’s therefore essential that
the copy behaves equivalently to the original, or the result may not be what’s expected.

译文:
代码中,提供的函数对象会复制到新线程的存储空间当中,函数对象的执行和调用都在线程的内存空间中进行。函数对象的副本与原始函数对象保持一致,否则得到的结果会与我们的期望不同。

问题:
It’s therefore指的是,所以……(blah,blah,blah)
这句话的意思是
因此,副本与原件是严格一致的,不然会出现意料之外的结果。

中文版2.1章这里是不是有翻译错误?

This breaks the association of the thread with the std::thread object and ensures that std::terminate() won’t be called when the std::thread object is destroyed, even though the thread is still running in the background.

现在的翻译是:

如果不想等待线程结束,可以分离线程,从而避免异常。不过,这就打破了线程与std::thread对象的联系,即使线程仍然在后台运行着,分离操作也能确保std::terminate()在std::thread对象销毁时才调用。
实际上应该是确保std::terminate()在std::thread对象销毁不会被调用。

3.2.5开始一段不太准确

you can create deadlock with two threads and no locks by having each thread call join() on the std::thread object for the other

应该是两个线程互相调用join

3.2.4节翻译比较难理解的地方(或许不太对)

在3.2.4的第三段话,原文是

But if a fixed order is chosen (for example, the mutex for the instance supplied as the first parameter, then the mutex for the instance supplied as the second parameter), this can backfire: all it takes is for two threads to try to exchange data between the same two instances with the parameters swapped, and you have deadlock!

翻译的是:

不过,选择一个固定的顺序(例如,实例提供的第一互斥量作为第一个参数,提供的第二个互斥量为第二个参数),可能会适得其反:在参数交换了之后,两个线程试图在相同的两个实例间进行数据交换时,程序又死锁了!

问题主要在后半句话,但我感觉作者想表达的意思是:有两个线程需要运行该operation,第二个线程的参数和第一个线程参数的顺序是相反的,而不是说参数交换之后,这个swapped应该是指,例如第一个线程参数顺序是
exchange(A,B),第二个线程参数顺序是exchange(B,A)
然后1A上锁,2B上锁,然后当1B试图上锁时,发生了死锁

6.2.1死锁不理解的地方

”当调用持有一个锁的用户代码时,有两个地方可能会死锁:拷贝构造或移动构造(①,③)和拷贝赋值或移动赋值操作⑤;还有一个潜在死锁的地方在于用户定义的new操作符。无论是以直接调用栈的成员函数的方式,还是在成员函数进行操作时,对已经插入或删除的数据进行操作的方式,对锁进行获取,都可能造成死锁。不过,用户要对栈负责,当栈未对数据进行拷贝或分配时,用户就不能随意的将其添加到栈中。“
请问这里①,③,还有⑤为什么会发生死锁,每次调用函数开头就已经上锁了,如果为空就直接抛出,那为什么还会死锁。没明白,能否举例说明哈。谢谢!

2.3 转移线程所有权

第一段”假设要写一个在后台启动线程的函数,并想通过新线程返回的所有权去调用这个函数,而不是等待线程结束再去调用“对应原文为"Suppose you want to write a function that creates a thread to run in the background, but passes ownership of the new thread back to the calling function rather than waiting for it to complete",疑有误。

翻译有误

你好,首先感谢您所做的工作!
1.在第九章代码9.2末尾的注释我的理解应该是“其余同前”的意思,这样是否更合理
2.在清单9.2下面的“因为能并发执行,最小工作块值的一试”,应该是”值得一试“

翻译错误:2.1.1

原文如下:
This is about as simple as it gets. Of course, you have to make sure that the
header is included so the compiler can see the definition of the std::thread class. As
with much of the C++ Standard Library, std::thread works with any callable type, so
you can pass an instance of a class with a function call operator to the std::thread
constructor instead:

译文如下:
为了让编译器识别std::thread类,这个简单的例子也要包含头文件。如同大多数C++标准库一样,std::thread可以用可调用类型构造,将带有函数调用符类型的实例传入std::thread类中,替换默认的构造函数。

错误位置:
so you can pass an instance of a class with a function call operator to the std::thread
constructor instead
这里指的并不是替换默认构造函数,这个instead指的是用类实例的函数调用运算符替换普通的函数,就是前面定义的do_some_work函数。

8.2.2

原翻译

counter变量是全局的,所以任何线程都能调用processing_loop()去修改同一个变量。因此,当新增加的处理器时,counter变量必须要在缓存内做一份拷贝,再改变自己的值,或其他线程以发布的方式对缓存中的拷贝副本进行更新

原文

The counter is global, so any threads that call processing_loop() are modifying the same variable. Therefore, for each increment the processor must ensure it has an upto-date copy of counter in its cache, modify the value, and publish it to other processors.

建议

counter变量是全局的,所以任何调用procecssing_loop的线程都是在修改同一个变量。因此,每次对counter进行增量操作时,处理器都必须确保缓存中的counter是最新的值,然后修改告知其他处理器

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.