CATEGORII DOCUMENTE |
Asp | Autocad | C | Dot net | Excel | Fox pro | Html | Java |
Linux | Mathcad | Photoshop | Php | Sql | Visual studio | Windows | Xml |
There are two main reasons why any .NET developer needs to keep synchronization in mind when designing a multithreaded application:
To avoid race conditions
To ensure threadsafety
Since the .NET Framework has built-in support for threading, there is a possibility that any class you develop may eventually be used in a multithreaded application. You don't need to (and shouldn't) design every class to be thread-safe, because thread safety doesn't come for free. But you should at least think about thread safety every time you design a .NET class. The costs of thread safety and guidelines concerning when to make classes thread-safe are discussed later in the chapter. You need not worry about multithreaded access to local variables, method parameters, and return values, because these variables reside on the stack and are inherently thread-safe. But instance and class variables will only be thread-safe if you design your class appropriately.
Before we examine the nuts and bolts of synchronization, let's consider in detail the ATM example that we discussed at the beginning of the chapter. Figure 1 depicts with more clarity the ATM scenario where Mr. X and Mrs. X are both trying to withdraw the last $1,000 from the same account at the same time. Such a condition, where one thread accesses a resource and leaves it in an invalid state while at the same time another thread uses the object when in an invalid state to produce undesirable results, is called a race condition. To avoid the race condition, we need to make the Withdraw() method thread-safe so that only one thread can access the method at any point of time.
There are at least three ways to make an object thread-safe:
Synchronize critical sections within the code
Make the object immutable
Use a thread-safe wrapper
To avoid undesirable effects caused by multiple threads updating a resource at the same time, we need to restrict access to that resource such that only one thread can update the resource at any point of time, or in other words, make the resource thread-safe. The most straightforward way to make an object or an instance variable thread-safe is to identify and synchronize its critical sections. A critical section is a piece of code in the program that may be accessed by multiple threads at the same time to update the state of the object. For example, in the above scenario where Mr. X and Mrs. X are both trying to access the same Withdraw() method at the same time, the Withdraw() method becomes the critical section and needs to be thread-safe. The easiest way to do this is to synchronize the method Withdraw() so that only one thread (either Mr. X or Mrs. X) can enter it at any one time. A process that cannot be interrupted during its execution is said to be Atomic. An atom (in the classical meaning of the word) is an indivisible unit, and atomic processes are units of code that execute as one complete unit - as if they were a single processor instruction. By making the Withdraw() method atomic, we ensure that it is not possible for another thread to change the balance of the same account until the first thread has finished changing the state of the account (emptying in our case). The following code listing is a pseudo-code representation of a non-thread-safe Account class:
public class AccountThis next listing represents a thread-safe pseudo-code version of the Account class:
public class AccountIn the first listing, two or more threads can enter the critical section at the same time so there is a possibility that both the threads check the balance at the same time, with both the threads receiving the balance ($1,000) of the account. Due to this, there is a possibility that the ATM might dispense the $1,000 amount to both the users, thus causing the account to go overdrawn unexpectedly.
However, in the second listing, only one thread is allowed access to the critical section at any one time. Assuming that Mr. X's thread gets the first slice of time, Mr. X's thread will enter the Withdraw() method just before Mrs X's. So, when Mr. X's thread begins to execute the Withdraw() method, Mrs. X's thread is not allowed access to the critical section and has to wait until Mr. X's thread leaves the section. As a result, Mr. X's thread checks the balance of the account, updates the account with the new balance, which is $0 in this case, and then returns the approval Boolean value (true in this case) to the ATM for dispensing the cash. Until the cash is dispensed, no other thread has access to the critical section of Mr. and Mrs. X's Account object. After Mr. X receives the cash, Mrs. X's thread enters the critical section of the Withdraw() method. Now, when the method checks for the account balance, the returned amount is $0 and, as a result, the method returns a Boolean value of false indicating insufficient balance and the ATM denies the withdrawal.
An alternative way to make an object thread-safe is to make the object immutable. An immutable object is one whose state can't be changed once the object has been created. This can be achieved by not allowing any thread to modify the state of the Account object once it is created. In this approach, we separate out the critical sections that read the instance variables from those that write to instance variables. The critical sections that only read the instance variables are left as they are, whereas the critical sections that change the instance variables of the object are changed so that, instead of changing the state of the current object, a new object is created that embodies the new state, and a reference to that new object is returned. In this approach, we don't need to lock the critical section because no methods (only the constructor) of an immutable object actually writes to the object's instance variables, thus, an immutable object is by definition thread-safe.
The third approach to making an object thread-safe is to write a wrapper class over the object that will be thread-safe rather than making the object itself thread-safe. The object will remain unchanged and the new wrapper class will contain synchronized sections of thread-safe code. The following listing is a wrapper class over the Account object:
public class AccountWrapperThe AccountWrapper class acts as a thread-safe wrapper of the Account class. The Account instance is declared as a private instance variable of the AccountWrapper class so that no other object or thread can access the Account variable. In this approach, the Account object does not have any thread-safe features, since all the thread-safety is provided by the AccountWrapper class.
This approach is typically useful when you are dealing with a third-party library and the classes in that library are not designed for thread safety. For example, let's assume that the bank already has an Account class that it used for developing software for its mainframe system and, for the sake of consistency, wants to use the same Account class for writing the ATM software. From the documentation of the Account class that the bank has provided us, it is clear that the Account class is not thread-safe. Also, we are not given access to the Account source code for security reasons. In such a case, we would have to adopt the thread-safe wrapper approach where we develop the thread-safe AccountWrapper class as an extension to the Account class. Wrappers are used to add synchronization to non-thread-safe resources. All the synchronization logic will be in the wrapper class and keeping the non-thread-safe class intact.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 694
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved