Thread Safety of C # Learning notes

Source: Internet
Author: User
Tags message queue

Thread Safety

A program and method in the face of any multi-threaded case is not uncertain, then is thread-safe. Thread safety is accomplished primarily by locking and reducing the likelihood of interaction between threads.

Generic types are rarely fully thread-safe due to the following several reasons:

    • Thread-safe development burdens are very heavy, especially if a type has many fields (each field potentially has many threads interacting with the root).
    • Thread safety can degrade performance (correctly, see if it is used in multiple threads).
    • Thread safety is not necessarily a thread-safe type to use.

Therefore, thread safety is only implemented where it is needed.

However, there are ways to "cheat" and have large and complex classes safely running in a multithreaded environment. One is to sacrifice granularity by encapsulating large pieces of code--or even accessing the entire object--in a repulsive lock, forcing the serialization of the upper layer to access it. In fact, this strategy is critical when using third-party unsafe code (most framework types). This technique simply uses an identical lock to protect all fields, properties, and methods of a non-thread-safe object. If the method of this object executes very quickly, then this solution works very well (otherwise, it will be heavily blocked).

Outside of the original type, very few framework classes are thread-safe, and this guarantee should be the responsibility of the developer, usually using an exclusion lock.

Another approach is to minimize the interaction between threads by minimizing the sharing of data. This is a very good method that is often used in a stateless middle tier or Web Services page. Because multiple client requests can arrive at the same time, the service method must be thread-safe. A stateless design (because extensibility is very popular) inherently limits the likelihood of interaction, because classes do not persist data between requests. Thread interaction is only used to create static fields that cache commonly used data in memory and provide basic services such as authorization and auditing.

The last method is to use an atomic lock. NET Framework is implemented if you subclass the ContextBoundObject and use the synchronization property on the class. Then the properties and methods of the object will take an atomic lock on an object whenever it is called, and during the execution of the method and property. While this reduces the burden of thread safety, it also poses its own problems: deadlocks do not occur, poor concurrency, and unexpected reentrant behavior. For these reasons, manual locking is a better choice-at least until a simple automatic lock can be used.

Thread-safe and. NET Framework Types

Locks allow non-thread-safe code to become thread-safe. The NET Framework is a good application: almost all non-primitive types are not thread-safe when instantiated (it is safe to read only), but they can be used in multithreaded code if you secure access to it through a lock. Here is an example where two threads add an item to the same list, and then enumerate the list:

Safethreadexample

In this example we lock the list itself. If there are two related lists, then we have to choose to lock on a generic object (the better way is to use a separate field).

Enumeration. NET collection is also thread-safe and throws an exception if the list is modified during enumeration. In this example, we copy the item into an array instead of locking the list during enumeration. If the enumeration process is time-consuming, this avoids excessive ownership of the lock (another solution is to use a read-write lock).

Lock around objects

Sometimes you need to lock around the object. Imagine, suppose. NET list is thread-safe and we want to add an item to the list:

if (!_list. Contains (NewItem)) _list. ADD (NewItem);

The statement itself is not thread-safe, regardless of whether the list is thread-safe. To prevent the test list from being preempted by other threads when it contains new items and adding new items, the entire if statement must be placed in lock. The same lock industry needs to be placed anywhere the list is modified. The following sentence also needs to be placed in the lock: _list. Clear (); To ensure that it is not preempted. In other words, we have to use it as a thread-unsafe collection class locking (which makes it redundant to assume that list is thread safe).

Locking around Access collection objects makes it too congested in high-concurrency environments. So far, the 4.0 framework provides a thread-safe queue,stack and dictionary.

Static methods

A custom lock is used to encapsulate access to an object only if all concurrent threads realize-and use-locks. If the socpe of an object is broad, it is not always true. The worst case scenario is that a public type contains static members. Imagine that if the static field DateTime.Now of a datetime struct is not thread-safe, two concurrent calls will result in a confusing output or an exception. The only way is for the external lock to lock the type itself--lock (typeof (DateTime))--Before calling DateTime.Now. If the programmer agrees to do so, then there is no problem (the fact is unlikely). Moreover, the locking type itself has brought its own problems.

For this reason, static members must be carefully programmed to meet thread safety. The common design in the NET Framework is that static members are currently secure and instantiated members are not thread-safe. It makes sense to write access to the public type in this mode so that it doesn't make a thread-safety challenge. In other words, by making the static function thread safe, you are programming so as not to interfere with its use.

The thread safety of a static function is not an advantage of itself, but rather requires you to write the code explicitly.

Read-only thread-safe

Making a type safe for read-only access is advantageous, because it means using it without overly locking. A few. NET type follows this principle: for example, a collection is thread-safe for concurrent reads.

Following this principle is simple: if you write a document that records a type that is type-safe for concurrent reads, then there is no need to write in the body of the function, and the user expects it to be thread-safe. For example, in the ToArray () function implementation of a collection, you might do so by compressing the internal structure. However, this will be thread-safe for the consumer to expect read-only.

Read-only thread safety is an enumerator and one reason to enumerate splits: 2 threads can enumerate a collection at the same time, because each has a separate enumerator object.

Because the document is missing, you need to be extra careful whether a function is read-only thread safe. Like the random class: When you call Random.next (), its internal implementation requires that the private seed value be updated. Therefore, you must lock around the random class, or a separate object for each thread.

Thread Safety for server applications

Server applications require multiple threads to handle multiple concurrent clients. WCF, ASP. NET and Web Services applications are obvious examples, as are the remoting server applications that use HTTP or TCP. This means that you write the server segment code and you have to consider thread safety if there is any possible interaction between the threads that handle the client request. Fortunately, this is a very small possibility; A typical server class is stateless (no fields) or has an active model that creates a separate object model for each request. Interactions are often triggered by static fields, sometimes using caching to improve performance.

As an example, suppose you have a Retrieveuser method to query the database:

// user is a custom class with fields for User data Internal User retrieveuser (int id) {...}

If this method is often called, you should improve performance by caching the data in a static dictionary. This is a solution that takes a thread-safe approach:

Static classusercache{Staticdictionary<int,user> _users =Newdictionary<int,user>(); Internal StaticUser GetUser (intid) {User u=NULL; Lock(_user) {if(_user. TryGetValue (ID, outu))returnu; } u=retrieveuser (ID);///Method to retrieve from database.        Lock(_users) _users[id]=u; returnu; }}

We must minimally use locks to read and write or update the dictionary to ensure that it is thread safe. In this example, we have a tradeoff between performance and simplicity. Our design actually has very small potential inefficiencies: If 2 threads call this function at the same time with the same ID that is not found, then Retrieveuser may be called 2 times-The dictionary will be updated unnecessarily. If a lock crosses the entire function, this will not happen, but creates a worse inefficiency: the entire cache will be locked during the call to Retrieveruser, and the other threads will be blocked regardless of whether or not you are querying other users.

Rich client and thread affinity

Either WPF or the Window form library follows a model that gives thread affinity. Although each one has its own independent implementation, how they work is very similar.

The object that paints the rich client is primarily based on the WPF dependency property (DEPENDENCYOBJEC) or the control of the window form. These objects have thread-pass dependencies, which means that only the threads that instantiate them can access their members. Violating this principle will cause an unexpected error or throw an exception.

On the positive side, you can access a UI object without having to lock it. On the negative side, if you want to invoke the member of Object X in the y thread, you must set (Marshal) this request to thread Y. You can do this explicitly by using the following methods:

    • In WPF, invoke Invoke or BeginInvoke on the dispatcher object of the element.
    • In WF, invoke Invoke or BeginInvoke on the control.

Both invoke and BeginInvoke accept a delegate that references the method on the target control that you want to run. Invoke synchronous operation: The caller blocks until the function finishes executing. BeginInvoke asynchronous work: The caller returns immediately, and the request is pressed into the queue (using the same message queue that handles the keyboard, mouse and timer).

Suppose we have a form that contains a text box called Txtmessage, and we want to update its contents from a worker thread, here's an example of WPF:

 Public Partial classmywindow:window{ PublicMywindow () {InitializeComponent (); NewThread (work).    Start (); }    voidWork () {Thread.Sleep ( the); Updatemessage ("The answer"); }    voidUpdatemessage (stringmessage) {Action Action= () =>txtmessage.text=message;    DISPATCHER.INVOKD (action); }}

WF code is similar to WPF, in addition to invoking the form's invoke instead.

    void Updatemessage (string  message)    {        action action= () =>txtmessage.text=message;          This . INVOKD (action);    }

The framework provides 2 ways to streamline this process:

    • Background worker thread (backgroundworkder)
    • Tasks (Task)

Worker threads VS UI Threads

Think about it. A rich client has 2 different types of threads that are extremely helpful: UI threads and worker threads. The UI thread ("owning") instantiates the UI element, and the worker thread does not. Worker threads typically perform tasks such as extracting data for a long time.

Most rich client programs have only one UI thread (also called the main thread) and occasionally produce worker threads--also directly or using background worker threads. These worker threads return to the main thread in order to update or report a progress column set.

So, when should an application have more than one UI thread? The main scenario is when you want to have multiple top-level forms, often called SDI (single Document Interface) programs, such as word. Each SDI form usually shows itself as a standalone application in the taskbar and is mostly isolated from other SDI forms. Given a form like this that has a UI thread, the application can respond more.

Immutable Objects (cannot become object)

A non-becoming object means that its state cannot be changed-both internally and externally. This non-becoming field is usually declared as read-only and fully initialized at construction time.

Immutable is a markup for functional programming--instead of mutable objects with different properties. LINQ follows this example. Immutable is also valuable in multi-threading, which avoids the problem of shared writing-by eliminating (or minimizing) shared writes.

One of the principles of using a none object is to encapsulate a set of related fields to minimize the lock cycle. According to a simple example, suppose we have 2 fields:

int _percentcomplete;

String _statusmessage;

And we want to read and write them in an atomic nature. We can use the following way instead of Chega around these words:

classProgressstatus//represents progress of some activity{     Public ReadOnly intPercentComplete;  Public ReadOnly stringStatusMessage; //This class might has many more fields ...     PublicProgressstatus (intPercentComplete,stringStatusMessage) {PercentComplete=PercentComplete; Statismessage=StatusMessage; }}

Then we define one type of single field that accompanies the lock object: ReadOnly-Object _statuslocker = new Object (); Progressstatus _status;

We can read and write that type of value without having to have a lock.

Note This is a method that does not require a lock to prevent a set of related properties from being inconsistent. But this does not prevent it from being changed when you use it-for this reason you need a lock. At Part5, we'll see more examples of using immutability to simplify multithreading-including PLINQ.

Thread Safety of C # Learning notes

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.