multithreading 术语“线程安全”是什么意思?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/261683/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:59:36  来源:igfitidea点击:

What is the meaning of the term "thread-safe"?

multithreadinglanguage-agnosticprogramming-languagesconcurrency

提问by Varun Mahajan

Does it mean that two threads can't change the underlying data simultaneously? Or does it mean that the given code segment will run with predictable results when multiple threads are executing that code segment?

这是否意味着两个线程不能同时更改底层数据?或者这是否意味着当多个线程正在执行该代码段时,给定的代码段将以可预测的结果运行?

采纳答案by Blauohr

From Wikipedia:

来自维基百科:

Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads. In particular, it must satisfy the need for multiple threads to access the same shared data, and the need for a shared piece of data to be accessed by only one thread at any given time.

There are a few ways to achieve thread safety:

Re-entrancy:

Writing code in such a way that it can be partially executed by one task, reentered by another task, and then resumed from the original task. This requires the saving of state information in variables local to each task, usually on its stack, instead of in static or global variables.

Mutual exclusion:

Access to shared data is serialized using mechanisms that ensure only one thread reads or writes the shared data at any time. Great care is required if a piece of code accesses multiple shared pieces of data—problems include race conditions, deadlocks, livelocks, starvation, and various other ills enumerated in many operating systems textbooks.

Thread-local storage:

Variables are localized so that each thread has its own private copy. These variables retain their values across subroutine and other code boundaries, and are thread-safe since they are local to each thread, even though the code which accesses them might be reentrant.

Atomic operations:

Shared data are accessed by using atomic operations which cannot be interrupted by other threads. This usually requires using special machine language instructions, which might be available in a runtime library. Since the operations are atomic, the shared data are always kept in a valid state, no matter what other threads access it. Atomic operations form the basis of many thread locking mechanisms.

线程安全是一个适用于多线程程序上下文的计算机编程概念。如果一段代码在多个线程同时执行期间正确运行,则它是线程安全的。特别是,它必须满足多个线程访问同一共享数据的需要,以及在任何给定时间只有一个线程访问共享数据的需要。

有几种方法可以实现线程安全:

重入:

以这样一种方式编写代码:它可以由一个任务部分执行,由另一个任务重新输入,然后从原始任务中恢复。这需要将状态信息保存在每个任务的局部变量中,通常在其堆栈中,而不是在静态或全局变量中。

互斥:

对共享数据的访问使用确保在任何时候只有一个线程读取或写入共享数据的机制进行序列化。如果一段代码访问多个共享的数据段,则需要非常小心——问题包括竞争条件、死锁、活锁、饥饿以及许多操作系统教科书中列举的各种其他问题。

线程本地存储:

变量被本地化,以便每个线程都有自己的私有副本。这些变量跨子例程和其他代码边界保留它们的值,并且是线程安全的,因为它们对于每个线程都是本地的,即使访问它们的代码可能是可重入的。

原子操作:

共享数据通过不能被其他线程中断的原子操作访问。这通常需要使用特殊的机器语言指令,这些指令可能在运行时库中可用。由于操作是原子的,因此无论其他线程访问它,共享数据始终保持有效状态。原子操作构成了许多线程锁定机制的基础。

read more:

阅读更多:

http://en.wikipedia.org/wiki/Thread_safety

http://en.wikipedia.org/wiki/Thread_safety



回答by Marek Blotny

Thread-safe code is code that will work even if many Threads are executing it simultaneously.

线程安全代码是即使许多线程同时执行也能工作的代码。

http://mindprod.com/jgloss/threadsafe.html

http://mindprod.com/jgloss/threadsafe.html

回答by Charles Bretana

A more informative question is what makes code not thread safe- and the answer is that there are four conditions that must be true... Imagine the following code (and it's machine language translation)

一个信息量更大的问题是什么使代码不是线程安全的——答案是有四个条件必须为真……想象一下下面的代码(它是机器语言翻译)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. The first condition is that there are memory locations that are accessible from more than one thread. Typically, these locations are global/static variables or are heap memory reachable from global/static variables. Each thread gets it's own stack frame for function/method scoped local variables, so these local function/method variables, otoh, (which are on the stack) are accessible only from the one thread that owns that stack.
  2. The second condition is that there is a property (often called an invariant), which is associated with these shared memory locations, that must be true, or valid, for the program to function correctly. In the above example, the property is that “totalRequests must accurately represent the total number of times any thread has executed any part of the increment statement”. Typically, this invariant property needs to hold true (in this case, totalRequests must hold an accurate count) before an update occurs for the update to be correct.
  3. The third condition is that the invariant property does NOT hold during some part of the actual update. (It is transiently invalid or false during some portion of the processing). In this particular case, from the time totalRequests is fetched until the time the updated value is stored, totalRequests does notsatisfy the invariant.
  4. The fourth and final condition that must occur for a race to happen (and for the code to therefore NOTbe "thread-safe") is that another thread must be able to access the shared memory whilethe invariant is broken, thereby causing inconsistent or incorrect behavior.
  1. 第一个条件是存在可从多个线程访问的内存位置。通常,这些位置是全局/静态变量或可从全局/静态变量访问的堆内存。每个线程都为函数/方法范围的局部变量获取自己的堆栈帧,因此这些局部函数/方法变量 otoh(位于堆栈上)只能从拥有该堆栈的一个线程访问。
  2. 第二个条件是存在与这些共享内存位置相关联的属性(通常称为不变量),该属性必须为真或有效,程序才能正常运行。在上面的例子中,属性是“ totalRequests 必须准确地表示任何线程执行了增量语句的任何部分的总次数”。通常,在更新发生之前,此不变属性需要保持为真(在这种情况下,totalRequests 必须保持准确的计数)才能使更新正确。
  3. 第三个条件是不变属性在实际更新的某些部分不成立。(它在处理的某些部分暂时无效或错误)。在这个特定的情况下,从时间totalRequests被取出,直到更新的值存储的时间,totalRequests并不能满足不变量。
  4. 第四个也是最后一个条件是竞争发生(并且代码因此不是“线程安全”的)是另一个线程必须能够在不变量被破坏访问共享内存,从而导致不一致或不正确的行为。

回答by Buu Nguyen

I like the definition from Brian Goetz's Java Concurrency in Practice for its comprehensiveness

我喜欢 Brian Goetz 的 Java Concurrency in Practice 中的定义,因为它的全面性

"A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code."

“如果一个类在从多个线程访问时行为正确,则该类是线程安全的,无论运行时环境对这些线程的执行进行调度或交错,并且调用代码部分没有额外的同步或其他协调。 ”

回答by Marcus Downing

As others have pointed out, thread safety means that a piece of code will work without errors if it's used by more than one thread at once.

正如其他人指出的那样,线程安全意味着如果一段代码同时被多个线程使用,它就可以正常工作而不会出错。

It's worth being aware that this sometimes comes at a cost, of computer time and more complex coding, so it isn't always desirable. If a class can be safely used on only one thread, it may be better to do so.

值得注意的是,这有时会以计算机时间和更复杂的编码为代价,因此并不总是可取的。如果一个类只能在一个线程上安全使用,那么这样做可能会更好。

For example, Java has two classes that are almost equivalent, StringBufferand StringBuilder. The difference is that StringBufferis thread-safe, so a single instance of a StringBuffermay be used by multiple threads at once. StringBuilderis not thread-safe, and is designed as a higher-performance replacement for those cases (the vast majority) when the String is built by only one thread.

例如,Java 有两个几乎等价的类,StringBufferStringBuilder. 不同之处在于它StringBuffer是线程安全的,因此StringBuffer多个线程可以同时使用a 的单个实例。StringBuilder不是线程安全的,当 String 仅由一个线程构建时,它被设计为这些情况(绝大多数)的更高性能替代品。

回答by Mnementh

Thread-safe-code works as specified, even when entered simultaneously by different threads. This often means, that internal data-structures or operations that should run uninterrupted are protected against different modifications at the same time.

线程安全代码按规定工作,即使由不同线程同时输入。这通常意味着,应该不间断运行的内部数据结构或操作同时受到保护,免受不同的修改。

回答by Hapkido

An easier way to understand it, is what make code not thread-safe. There's two main issue that will make a threaded application to have unwanted behavior.

一种更简单的理解方法是使代码不是线程安全的。有两个主要问题会使线程应用程序出现不需要的行为。

  • Accessing shared variable without locking
    This variable could be modified by another thread while executing the function. You want to prevent it with a locking mechanism to be sure of the behavior of your function. General rule of thumb is to keep the lock for the shortest time possible.

  • Deadlock caused by mutual dependency on shared variable
    If you have two shared variable A and B. In one function, you lock A first then later you lock B. In another function, you start locking B and after a while, you lock A. This is a potential deadlock where first function will wait for B to be unlocked when second function will wait for A to be unlocked. This issue will probably not occur in your development environment and only from time to time. To avoid it, all locks must always be in the same order.

  • 在不加锁的情况下访问共享变量
    此变量可以在执行函数时被另一个线程修改。你想用锁定机制来防止它,以确保你的函数的行为。一般的经验法则是保持锁定的时间尽可能短。

  • 共享变量相互依赖导致的死锁
    如果你有两个共享变量 A 和 B。在一个函数中,你先锁定 A,然后锁定 B。在另一个函数中,你开始锁定 B,过了一会儿,你锁定 A。这个是一个潜在的死锁,第一个函数将等待 B 解锁,而第二个函数将等待 A 解锁。此问题可能不会出现在您的开发环境中,只会偶尔出现。为避免这种情况,所有锁必须始终处于相同的顺序。

回答by Bill the Lizard

Yes and no.

是和否。

Thread safety is a little bit more than just making sure your shared data is accessed by only one thread at a time. You have to ensure sequential access to shared data, while at the same time avoiding race conditions, deadlocks, livelocks, and resource starvation.

线程安全不仅仅是确保您的共享数据一次只能被一个线程访问。您必须确保对共享数据的顺序访问,同时避免竞争条件死锁活锁资源匮乏

Unpredictable results when multiple threads are running is nota required condition of thread-safe code, but it is often a by-product. For example, you could have a producer-consumerscheme set up with a shared queue, one producer thread, and few consumer threads, and the data flow might be perfectly predictable. If you start to introduce more consumers you'll see more random looking results.

多线程运行时的不可预知的结果不是线程安全代码的必要条件,但它通常是一个副产品。例如,您可以设置一个生产者-消费者方案,其中包含一个共享队列、一个生产者线程和几个消费者线程,并且数据流可能是完全可预测的。如果您开始引入更多的消费者,您会看到更多随机的结果。

回答by assylias

In essence, many things can go wrong in a multi threaded environment (instructions reordering, partially constructed objects, same variable having different values in different threads because of caching at the CPU level etc.).

本质上,在多线程环境中,很多事情都可能出错(指令重新排序、部分构造的对象、由于 CPU 级别的缓存而在不同线程中具有不同值的相同变量等)。

I like the definition given by Java Concurrency in Practice:

我喜欢Java Concurrency in Practice给出的定义:

A [portion of code] is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.

一个[部分代码]是线程安全的,如果它在从多个线程访问时表现正确,而不管运行时环境对这些线程的执行进行调度或交错,并且没有额外的同步或其他协调调用代码。

By correctlythey mean that the program behaves in compliance with its specifications.

通过正确的他们是指以符合其规范的程序的行为。

Contrived example

人为的例子

Imagine that you implement a counter. You could say that it behaves correctly if:

想象一下,您实现了一个计数器。如果出现以下情况,您可以说它的行为正确:

  • counter.next()never returns a value that has already been returned before (we assume no overflow etc. for simplicity)
  • all values from 0 to the current value have been returned at some stage (no value is skipped)
  • counter.next()从不返回之前已经返回的值(为了简单起见,我们假设没有溢出等)
  • 在某个阶段返回了从 0 到当前值的所有值(没有跳过任何值)

A thread safe counter would behave according to those rules regardless of how many threads access it concurrently (which would typically not be the case of a naive implementation).

线程安全计数器将根据这些规则运行,而不管有多少线程同时访问它(这通常不是简单实现的情况)。

Note: cross-post on Programmers

注意:程序员的交叉帖子

回答by Greg Balajewicz

Simply - code will run fine if many threads are executing this code at the same time.

简单 - 如果许多线程同时执行此代码,代码将运行良好。