C # Study Notes thread security,

Source: Internet
Author: User

C # Study Notes thread security,

Thread Security

A program and method are not uncertain in the face of any multithreading, so it is thread security. thread security is implemented mainly by locking and reducing the possibility of interaction between threads.

General types are rarely thread-safe for the following reasons:

  • The development burden of thread security is very heavy, especially when a type has many fields (each field may interact with many threads ).
  • Thread security reduces performance (to be correct, check whether it is used in multiple threads ).
  • Thread security does not have to use the thread security type.

Therefore, thread security is only implemented as needed.

However, there are some ways to "cheat" and run large and complex classes securely in a multi-threaded environment. One of them is to encapsulate large pieces of code to sacrifice granularity-or even access the complete object-in a rejection lock, forcing high-level serialize access to it. In fact, this policy is critical when using third-party Insecure code (most framework types ). This technique uses the same lock to protect all fields, attributes, and methods of a non-thread-safe object. If the method of this object is executed very quickly, this solution works very well (otherwise, it will block a lot ).

Except for the original type, few framework classes are thread-safe. This guarantee should be the responsibility of developers and is usually implemented using exclusion locks.

Another method is to minimize the interaction between threads by minimizing shared data. This is a very good method and is often used in stateless intermediate layers or Web service pages. Because multiple client requests can arrive at the same time, the service method must be thread-safe. A stateless design (very popular due to scalability) inherently limits the possibility of interaction because classes do not maintain data between requests. Thread interaction is only used to create static fields. It is used to cache commonly used data and provide basic services, such as authorization and audit, in the memory.

The last method is to use an atomic lock .. Net framework is implemented in this way, If You subclass ContextBoundObject and use the Synchronization attribute to the class. The attributes and methods of the object are called at any time, and an atomic lock of the object is used during the execution of methods and properties. Although this reduces the thread security burden, it also brings its own problems: deadlock does not occur, poor concurrency and unexpected reentrant. For these reasons, the manual lock is a better choice-at least before a simple automatic lock can be used.

 

Thread security and. NET Framework types

The lock can make non-thread-safe code thread-safe .. NET Framework is a good application: almost all non-primitive types are not thread-safe during instantiation (only read-only), but they can be used in multi-threaded code, if a lock is used to protect access to it. The following is an example. Two threads add an Item to the same List at the same time, and then enumerate the list:

SafeThreadExample

In this example, the list itself is locked. If there are two related lists, You have to select to lock a Common Object (a better way is to use an independent field ).

Enumeration. NET collections are thread-safe. If the list is modified during enumeration, an exception is thrown. In this example, copy the item to an array instead of locking the list during enumeration. If the enumeration process is time-consuming, this avoids having excessive locks (another solution is to use read/write locks ).

Lock around objects

Sometimes you need to lock the object. Imagine that the. NET List is thread-safe. We want to add an item to the list:

if(!_list.Contains(newItem))_list.Add(newItem);

Whether the list is thread-safe or not, the statement itself is not thread-safe. To prevent the entire if statement from being preemptible by other threads between the test list containing new items and addition of new items, the entire if statement must be placed in lock. The same lock also 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 preemptible. In other words, we have to lock it as a collection class that is not thread-safe (this makes it redundant to assume that list is thread-safe ).

Locking around the Access Collection object causes excessive blocking in High-concurrency environments. So far, the 4.0 framework provides a thread-safe queue, stack, and dictionary.

Static Method

A custom lock is used to encapsulate access to an object, which can be implemented only when all concurrent threads are aware of it and use the -- lock. If an object has a wide range of socpe, this is not always the case. The worst case is that a public type contains static members. Imagine that if the static field DateTime. Now of the DateTime struct is NOT thread-safe, two concurrent calls will lead to chaotic output or an exception. The only method is to use an external lock to lock the type itself -- lock (typeof (DateTime) -- before calling DateTime. Now. If programmers agree to do so, there is no problem (this is unlikely to happen ). In addition, the lock type also brings about its own problems.

For this reason, static members must be carefully programmed to ensure thread security .. The common design in the NET framework is that static members are currently secure, and instantiation members are not thread-safe. According to this mode, when writing and accessing the public type, it makes sense not to create thread security problems. In other words, by making the static function thread secure, you are programming to avoid interfering with its use.

The thread security of static functions is not its own advantage, but you need to explicitly write code.

Read-only thread security

This makes the type advantageous for the read-only access thread security (which is possible), because it means to use it without over-locking. Some. NET types follow this principle: for example, collections are thread-safe for concurrent reads.

It is easy to follow this principle: if you write a document to record a type that is safe for concurrent reads, you do not need to write it in the function body, and the user will expect it to be thread-safe. For example, in the ToArray () function implementation of a set, you may compress the internal structure. However, read-only is expected to be thread-safe for users.

Read-only thread security is one reason why an enumerator can be separated by enumeration: two threads can enumerate a set at the same time, because each has an independent enumerator object.

Due to lack of documentation, you need to be careful whether a function is read-only thread safe. Such as the Random class: When you call Random. Next (), its internal implementation requires updating the private seed value. Therefore, you must lock the Random class or create an independent object for each thread.

 

Thread security of server applications

The server application requires multiple threads to process multiple concurrent clients. WCF, ASP. NET, and Web Services applications are obvious examples. The Remoting server application using HTTP or TCP is also. This means that you write the server segment code. You must consider thread security if there is any possible interaction between threads that process client requests. Fortunately, this is very unlikely; a typical server class is stateless (with no fields) or there is an activity model that creates an independent object model for each request. Interactions are often triggered by static fields, and sometimes cache is used to improve performance.

For example, assume that you have a RetrieveUser method to query the database:

//User is a custom class with fields for user datainternal User RetrieveUser(int id){...}

If this method is often called, you should cache data in a static Dictionary to improve performance. This is a solution that considers thread security:

static class UserCache{    static Dictionary<int,User> _users = new Dictionary<int,User>();    internal static User GetUser(int id)    {        User u = null;        lock(_user)        {            if(_user.TryGetValue(id, out u)) return u;        }                u=RetrieveUser(id);    ///Method to retrieve from database.        lock(_users)_users[id]=u;        return u;    }}

We must use locks to read, write, or update dictionaries to a minimum to ensure thread safety. In this example, we have made a compromise between performance and simplicity. Our design is actually very small and potentially inefficient: If two threads call this function at the same time with the same unfound id, therefore, the RetrieveUser may be called twice-The dictionary will be updated as unnecessary. If a lock crosses the entire function, this will not happen, but it creates a worse inefficiency: The entire cache will be locked during RetrieverUser calls, other threads will be blocked whether or not you query other users.

 

Dependency between rich clients and threads

Both WPF and Window Form databases follow the model that gives thread relevance. Although each has its own independent implementation, how they work is very similar.

The objects of the rich client are mainly based on the Dependency Property (DependencyObjec) of WPF or the control of the Window Form ). These objects have thread relevance, which means that only the instantiated threads can access their members. Violating this principle will lead to unexpected errors or throw an exception.

On the positive side, you can access a UI object without locking it. On the negative side, if you want to call the member of object X in the Y thread, you must request the column set (Marshal) to thread Y. You can explicitly use the following methods to do this:

  • In WPF, Invoke or BeginInvoke is called on the Dispatcher object of the element.
  • In WF, Invoke or BeginInvoke is called on the control.

Both Invoke and BeginInvoke accept a delegate to reference the methods on the target control you want to run. Invoke synchronization: the caller is blocked until the function execution is complete. Ininvoke asynchronous operation: the caller returns immediately and the request is pushed into the queue (using the processing keyboard, the mouse and the Message Queue with the same timer ).

Suppose we have a form that contains a text box called txtMessage. We want to update its content from a working thread. Below is an example of WPF:

public partial class MyWindow : Window{    public MyWindow()    {        InitializeComponent();        new Thread(Work).Start();    }    void Work()    {        Thread.Sleep(5000);        UpdateMessage("The answer");    }    void UpdateMessage(string message)    {        Action action= ()=>txtMessage.Text=message;        Dispatcher.Invokd(action);    }}

The WF code is similar to WPF, except that the Invoke of the form is called.

    void UpdateMessage(string message)    {        Action action= ()=>txtMessage.Text=message;        this.Invokd(action);    }

The Framework provides two methods to simplify the process:

  • BackgroundWorkder)
  • Task)

Worker thread vs ui thread

Think about it. It is very helpful for a rich client to have two different types of threads: the UI thread and the working thread. The UI thread ("owns") instantiates the UI element. The worker thread does not. A worker thread usually executes a task for a long time, such as data extraction.

Most rich client programs only have one UI thread (also called the main thread) and occasionally generate a working thread-or directly use the background working thread. These worker threads return to the main thread for update or report progress columns.

So when should an application have multiple UI threads? The main scenario is that when you want to have multiple top-level forms, it is often called the SDI (Single Document Interface) program, such as Word. Each SDI form is usually displayed as an Independent Application on the taskbar and is mostly isolated from other SDI forms. Given a form with a UI thread, the application can respond more.

Immutable Objects (Objects cannot be changed)

An unchangeable object means that its State cannot be changed-either internal or external. This field cannot be declared as read-only and fully initialized during construction.

Immutable is a tag of functional programming-instead of a variable object with different attributes. LINQ follows this example. Immutable is also valuable in multithreading, which avoids the problem of shared write-by eliminating (or minimizing) shared write.

One principle of using non-essential objects is to encapsulate a set of related fields to minimize the lock cycle. In a simple example, assume that we have two fields:

Int _ percentComplete;

String _ statusMessage;

And we want to read and write them atomically. We can use the following method instead of locking around these fields:

class ProgressStatus //Represents progress of some activity{    public readonly int PercentComplete;    public readonly string StatusMessage;    // this class might have many more fields...    public ProgressStatus(int percentComplete, string statusMessage)    {        PercentComplete = percentComplete;        StatisMessage = statusMessage;    }}

Then we define a single field of that type, with 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 that this is a method that does not need to use locks to prevent inconsistency of a group of related attributes. But this cannot prevent it from being changed when you use it-for this reason, you need a lock. In Part5, we will see more examples of using immutability to simplify multithreading-including PLINQ.

 

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.