2012-03-05 56 views
7

我一直听说线程安全。那究竟是什么以及如何以及从哪里学习编写线程安全代码?线程安全编程

此外,假设我有2个线程,一个写入一个结构,另一个读取它。这有危险吗?有什么我应该找的?我不认为这是一个问题。这两个线程不会(完全不能)在同一时间访问该结构。

另外,有人可以告诉我如何在这个示例中:https://stackoverflow.com/a/5125493/1248779我们在并发问题上做得更好。我不明白。

+1

[Google]上的前几个匹配项(https://www.google.com/#hl=zh-CN&sclient=psy-ab&q=c%2B%2B+thread-safe+programming&pbx=1&oq=c%2B%2B +线程安全+编程与水性= F&AQI = AQL = gs_sm = 3&gs_upl = 532l4457l0l4950l27l12l0l2l2l0l481l3318l0.4.3.4.1l12l0&gs_l = hp.3 ... 532l4457l0l4950l27l12l0l2l2l0l481l3318l0j4j3j4j1l12l0&BAV = on.2,or.r_gc.r_pw.r_cp.r_qf,cf.osb&计划生育= 3e688b5b28c62a4d及白车身= 1306&bih = 867)似乎很有启发性。 – jogojapan 2012-03-05 07:14:37

+1

对不起,我试过了,我发现它太复杂了我需要一个简单的例子和​​workarround我猜 – Kam 2012-03-05 07:18:27

+0

为什么两个线程不能同时访问结构?除非你保护它(例如使用信号量),否则你不知道第一个线程是否写入了结构的一部分,然后OS调度器决定运行读取结构的线程,该线程现在不会被完全更新。 – 2012-03-05 07:21:23

回答

4

线程安全是“并发编程”的总标题下更大的一组问题的一个方面。我建议围绕这个主题阅读。

你假设两个线程不能同时访问结构是不好的。第一:今天我们有多核心机器,所以两个线程可以在同一时间运行。第二:即使在单个核心机器上,给予任何其他线程的时间片也是不可预测的。你必须预见那个“其他”线程可能正在处理的任意时间。见下面我的“机会之窗”的例子。

线程安全的概念正是为了回答“这是否有危险”的问题。关键问题是,在一个线程中运行的代码是否有可能获得某些数据的不一致视图,这种不一致的发生是因为它在运行另一个线程时正处于数据更改的中间。

在你的例子中,一个线程正在读取一个结构,同时另一个线程正在写入。假设有两个相关的领域:

{ foreground: red; background: black } 

和作家是改变这些

foreground = black; 
      <=== window of opportunity 
    background = red; 

如果读者在这一点机会之窗读取值的过程,然后它看到一个“胡说八道“组合

{ foreground: black; background: black } 

这种模式的本质这是很短的时间,而我们正在发生变化,系统变得不一致,读者不应该使用的值。一旦我们完成我们的更改,再次阅读变得安全。

因此,我们使用Stefan提到的CriticalSection API来防止线程看到不一致的状态。

+1

C#?这被标记为C++。 – Pubby 2012-03-05 07:21:50

+0

嗨,请检查该http://stackoverflow.com/a/5125493/1248779他说,这是你可以做的东西线程safe..I没有看到it..What不同的之前和之后incapsulation? – Kam 2012-03-05 07:49:54

+1

这是正确的,但甚至可能更糟糕。我不知道这些颜色是什么类型,但是如果它们不是基本的原子类型,那么您实际上直接在作业中获得了机会的_windows_。 – 2012-03-05 08:04:59

1

在WIN32平台上编写多线程C++程序时,需要保护某些共享对象,以便只有一个线程可以在任何给定时间从不同线程访问它们。您可以使用5个系统功能来实现此目的。它们是InitializeCriticalSection,EnterCriticalSection,TryEnterCriticalSection,LeaveCriticalSection和DeleteCriticalSection。

而且也许这个链接可以帮助: how to make an application thread safe?

http://www.codeproject.com/Articles/1779/Making-your-C-code-thread-safe

0

线程安全是当某个代码块被保护不被多个线程访问时。这意味着被操纵的数据总是保持一致的状态。

一个常见的例子是生产者消费者问题,其中一个线程的数据结构中读取,而另一个线程写入相同的数据结构:Detailed explanation

+0

这不是关于保护一段代码,而是关于保护一大块数据。 – 2012-03-05 07:36:52

+1

@Alex,好吧,它实际上都是。那一刻你涉及的设备(甚至只是控制台窗口或文件),你可能还需要锁定,即使你是不是在自己的程序修改任何数据。 – 2012-03-05 09:05:29

7

这是一个非常深刻的话题。在心脏线程通常是通过同时使用多个内核来让事情快速发展;或者如果您没有一种很好的方法将操作与“主”线程交错,则可以在后台执行长操作。后者在UI编程中很常见。

您的场景是经典麻烦点之一,也是第一批遇到的人之一。在成员真正独立的情况下建立一个结构是很少见的。想要修改结构中的多个值以保持一致性是很常见的。在没有任何预防措施的情况下,很有可能修改第一个值,然后让另一个线程读取结构并在第二个值写入之前对其进行操作。

简单的例子是2d图形的“点”结构。你想把这个点从[2,2]移到[5,6]。如果你有一个不同的线程绘制一条线,那么你可以很容易地画到[5,2]。

这是真正的冰山一角。有很多优秀的书籍,但学习这个空间通常会这样:

  1. 呃哦,我刚刚从不一致的状态读取该东西。
  2. 嗯哦,我只是从2个线程修改那件事,现在它是垃圾。
  3. 耶!我了解了锁
  4. 哇,我有很多的锁,当我有很多锁在嵌套代码中时,有时似乎只是有时候挂。
  5. Hrm。我需要停止在飞行中锁定这个锁定,我似乎错过了很多地方;所以我应该将它们封装在一个数据结构中。
  6. 这个数据结构很棒,但现在我似乎一直在锁,我的代码和单线程一样慢。
  7. 条件变量很奇怪
  8. 这很快,因为我巧妙地锁定了事物。人力资源管理。有时数据损坏。
  9. 哇......联锁了吗?你有没有?
  10. 嘿,看看没有锁,我做这个东西叫自旋锁。
  11. 条件变量。恩......我明白了。
  12. 你知道是什么,怎么样我就开始思考如何在完全独立的方式对这个东西进行操作,pipelineing我的操作,并具有尽可能少的横纱依赖尽可能...

显然,这是不所有关于条件变量。但是有很多问题可以通过线程解决,也可能有很多方法可以解决,甚至有更多的方法可以解决问题。

3

究竟是什么?

简而言之,可以在并发上下文中执行的程序,没有与并发有关的错误。

如果的ThreadA和ThreadB读和/或无差错写数据,并使用适当的同步,则程序可以是线程。这是一个设计选择 - 使对象线程安全可以通过多种方式完成,而更复杂的类型可以使用这些技术的组合进行线程安全。

,以及如何和我在哪里可以学习程序线程安全的代码?

boost/libs/thread /可能是一个很好的介绍。这个话题非常复杂。

C++的11个标准库提供了锁,原子能和线程的实现 - 它使用这些将是一个良好的阅读任何写得很好的程序。标准库是在boost实现之后建模的。

另外,假设我有2个线程,一个写入一个结构,另一个读取它。这有危险吗?有什么我应该找的?

是的,它可能是危险的和/或可能会产生不正确的结果。试想一下,线程可能会在任何时候耗尽时间,然后另一个线程可能会读取或修改该结构 - 如果您没有保护它,它可能正在更新中。一个常见的解决方案是锁,它可以用来防止另一个线程在读/写期间访问共享资源。

+0

正如我提到的一前一后请检查该stackoverflow.com/a/5125493/1248779他说,这是你可以做的东西线程safe..I没有看到it..What不同的之前和之后incapsulation? – Kam 2012-03-05 07:52:29

+0

@ user1248779西奥的'INT myFunc的(结构myState *状态)'不是线程安全的,如果'@a state'可能被另一个线程写入。如果'@a state'是线程本地数据,那么它将是线程安全的。哎呀,它不是封装,使得这个程序线程安全 - 这是因为对象'@a state'指向的是线程本地而不是全局。还要注意Theo确实指定'@a state'是线程本地的。 – justin 2012-03-05 08:13:15

+0

那他为什么要封装x和y并创建一个结构?有什么好处?请裸露我真的没有看到它 – Kam 2012-03-05 08:18:18

0

要回答这个问题的第二部分:想象一下,两个线程访问std::vector<int> data:并行

//first thread 
if (data.size() > 0) 
{ 
    std::cout << data[0]; //fails if data.size() == 0 
} 

//second thread 
if (rand() % 5 == 0) 
{ 
    data.clear(); 
} 
else 
{ 
    data.push_back(1); 
} 

运行这些线程,你的程序将会崩溃,因为std::cout << data[0];可能data.clear();后直接执行。

您需要知道,在线程代码的任何时候,线程可能会中断,例如,在检查(data.size() > 0)后,另一个线程可能会变为活动状态。虽然第一个线程在单线程应用程序中看起来是正确的,但它不在多线程程序中。

1

线程安全是一个简单的概念:它是“安全的”,以在一个线程,而另一个线程执行操作A的执行操作B,其可以是或可以不是相同的操作A.这可以被扩展到覆盖许多线程。在此背景下,“安全”是指:

  • 由螺纹

的实际操作A和B可以观察到没有未定义行为

  • 数据结构的所有不变量都保证是很重要的。如果两个线程读取一个普通的int变量,那么这很好。但是,如果有任何线程可能写入该变量,并且没有同步以确保读取和写入不能一起发生,那么您有一个数据竞争,这是未定义的行为,并且这是而不是线程安全。

    这同样适用于您询问的情况:除非您采取了特殊的预防措施,否则在另一个线程写入同一时间的同时,不得不从一个结构中读取一个线程。 如果你可以保证线程不能同时访问数据结构,通过某种形式的同步,如互斥锁,临界区,信号量或事件,那么就没有问题。

    可以使用的东西像互斥和临界区,以防止对某些数据的并发访问,这样写线程访问,当它被写入数据的唯一线程,读线程访问数据的时候才会线程它正在阅读,从而提供我刚刚提到的保证。这因此避免了上述未定义的行为。

    但是,您仍然需要确保您的代码在更广的上下文中是安全的:如果您需要修改多个变量,那么您需要在整个操作中保持互斥锁而不是每个单独的访问,否则你可能会发现你的数据结构的不变式可能不会被其他线程观察到。

    也有可能数据结构对于某些操作可能是线程安全的,但对其他操作可能不是。例如,如果一个线程正在推送队列中的项目,而另一个正在将项目从队列中弹出,但是如果两个线程正在推送项目,或者两个线程正在弹出项目,则单个生产者的单个使用者队列将会正常。

    在你引用的例子中,问题是全局变量在所有线程之间是隐式共享的,因此如果有任何线程可以修改它们,所有访问都必须通过某种形式的同步(例如互斥锁)来保护。另一方面,如果每个线程都有单独的数据副本,那么该线程可以修改其副本,而不用担心来自任何其他线程的并发访问,并且不需要同步。当然,如果两个或多个线程要在相同的数据上运行,您总是需要同步。

    我的书,C++ Concurrency in Action涵盖了什么是线程安全的东西,如何设计线程安全的数据结构,以及用于此目的的C++同步原语,如std::mutex