Thread safety: Difference between revisions
No edit summary |
m Change from unref to no-footnotes |
||
Line 1: | Line 1: | ||
{{ |
{{no footnotes|date=June 2009}} |
||
'''Thread safety''' is a [[computer programming]] concept applicable in the context of [[Thread (computer science)|multi-threaded]] programs. A piece of code is '''thread-safe''' if it can be used in a multi-threaded environment. |
'''Thread safety''' is a [[computer programming]] concept applicable in the context of [[Thread (computer science)|multi-threaded]] programs. A piece of code is '''thread-safe''' if it can be used in a multi-threaded environment. |
||
Revision as of 13:26, 15 December 2010
This article includes a list of references, related reading, or external links, but its sources remain unclear because it lacks inline citations. (June 2009) |
Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it can be used in a multi-threaded environment.
Thread safety is a key challenge in multi-threaded programming. It was not a concern of most application programmers for little home applications, but since the 1990s, as Windows become multithreading, and the expansion of BSD and Linux operating systems, has become a commonplace issue. In a multi-threaded program, several threads execute simultaneously in a shared address space. Every thread has access to virtually all the memory of every other thread. Thus the flow of control and the sequence of accesses to data often have little relation to what would be reasonably expected by looking at the text of the program, violating the principle of least astonishment. Thread safety is a property that allows code to run in multi-threaded environments by re-establishing some of the correspondences between the actual flow of control and the text of the program, by means of Process syncronization
Identification
It is not easy to determine if a piece of code is thread-safe or not. However, there are several indicators that suggest the need for careful examination to see if it is unsafe:
- accessing global variables or the heap
- allocating/reallocating/freeing resources that have global scope (files, sub-processes, pipes, etc.)
- indirect accesses through handles or pointers
- any visible side-effect (e.g., access to volatile variables in the C programming language)
A subroutine can be reentrant, and still not thread safe. For example, a routine to open a configuration file for reading, could be reentrant (not using any globar or static variable, and using only data passed as arguments) and still, if another thread writes to the file, race conditions and strange behavior may arise.
Implementation
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. There are still some rare cases where a static variable can be used in a reentrant function, if the access is done through atomic operations.
- Mutual exclusion or Process syncronization
- 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 and starvation
- 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.
Examples
In the following piece of C code, the function is thread-safe, but not reentrant
int function()
{
mutex_lock();
...
function body
...
mutex_unlock();
}
In the above, function
can be called by different threads without any problem. But if the function is used in a reentrant interrup handler and a second interrupt arraises inside the function, the second routine will hang forver. As interrupt servicing can disable other interrups, the whole system could suffer.
In the following piece of C code, the function is reentrant, but not thread-safe
int function()
{
char *filename="/etc/config";
FILE *config;
if(file_exist(filename)){
config=fopen(filename);
}
}
In the above, the function is perfectly reentrant, as it can be called any number of times and will not fail. But all the calls should be inside a single thread, where no file deletion or modification is done, in a controlled environment. If excecuted in a multi-thread environment, there is no waranty of what could another thread do to the configuration file.
Difficulties
One approach to making data thread-safe that combines several of the above elements is to make changes atomically to update the shared data. Thus, most of the code is concurrent, and little time is spent serialized.
See also
- Control flow analysis
- Priority inversion
- Concurrency control
- Exception safety
- Communicating sequential processes - a technique for analyzing concurrency
External links
- Thread-safe Tcl Extensions (wiki page)
- Article "Thread-safe webapps using Spring" by Steven Devijver
- Thread-safe design
- Article "Design for thread safety" by Bill Venners
- Article "Write thread-safe servlets" by Phillip Bridgham
- Article "Smart Pointer Thread Safety" by Dejan Jelovic
- A Short Guide to Mastering Thread-Safety