. NET multithreading using locks for synchronization and Task

Source: Internet
Author: User
Tags foreach exception handling mutex readline semaphore

. Multi-threaded Use task in net


Ask can be said to be the upgrade version of the ThreadPool, online task scheduling, parallel programming has a great role.


Create and initialize a task


To create a task using a lambda expression

Task.Factory.StartNew (() => Console.WriteLine ("Hello from a task!");

The var task = new Task (() => Console.Write ("Hello"));
Task. Start ();

  
Create a task with a delegate of the default parameter

Using System;
Using System.Threading.Tasks;

Namespace multithread
{
Class ThreadTest
{
static void Main ()
{
The var task = Task.Factory.StartNew (state => Greet ("Hello"), "greeting");
Console.WriteLine (Task.   asyncstate); Greeting
Task. Wait ();
}

static void Greet (String message) {console.write (message);}

}
}

  

One advantage of this approach is that task. AsyncState as a built-in property, you can get the state of a parameter in a different thread.


System.Threading.Tasks.TaskCreateOptions

When creating a task, we can specify some relevant options for creating a task. In. Net 4.0, you have the following options:
Longrunning

Used to indicate that the task is long-running, and that this parameter is more appropriate for the block thread. The longrunning thread is typically recycled for a long period, so the CLR may not put it in the thread pool for management.
Preferfairness

This means that the task runs as equitably as possible, avoiding the occurrence of some threads running too fast or too slowly.
AttachedToParent

Indicates that the task created is a subtask of the task on which the current thread resides. This is also a common use.

The following code is an example of creating a child task:

Using System;
Using System.Threading;
Using System.Threading.Tasks;

Namespace multithread
{
Class ThreadTest
{
public static void Main (string[] args)
{
Task parent = Task.Factory.StartNew (() =>
{
Console.WriteLine ("I am A parent");

Task.Factory.StartNew (() =>//Detached task
{
Console.WriteLine ("I am Detached");
});

Task.Factory.StartNew (() =>//Child task
{
Console.WriteLine ("I am A Child");
}, Taskcreationoptions.attachedtoparent);
});

Parent. Wait ();

Console.ReadLine ();
}

}
}

  

If you wait for a task to end, you must wait for the task to end. This is important, especially when you are using continue. (described later)


Wait for Task

The wait, which is not implemented in the ThreadPool built-in method, can be easily implemented in a task:

Using System;
Using System.Threading;
Using System.Threading.Tasks;

Namespace multithread
{
Class ThreadTest
{
static void Main ()
{
var T1 = Task.run (() => go (null));
var t2 = Task.run (() => Go (123));
Task.waitall (t1, T2);/wait for all task to end
Task.waitany (t1, T2);/wait for any task to end
}

static void Go (object data)//data is NULL with the "the".
{
Thread.Sleep (5000);
Console.WriteLine ("Hello from the thread pool!" + data);
}
}
}

  

Attention:

When you call a wait method, the current thread is blocked until the task returns. But if the task has not yet been executed, this time the system may use the current thread to execute the call task instead of creating a new one, so that there is no need to re-create a thread and block the current thread. This saves the cost of creating a new thread and avoids some thread switching. There are drawbacks, however, that if the current thread wants to obtain a lock at the same time as the task being invoked, it will cause a deadlock.


Task Exception Handling

When waiting for a task to complete (call wait or access to the result property), exceptions that are not handled in the task tasks are encapsulated into aggregateexception and thrown back. The InnerExceptions property encapsulates exceptions that are not handled by each task.

Using System;
Using System.Threading.Tasks;

Namespace Multithreadtest
{
Class Program
{
static void Main (string[] args)
{
int x = 0;
Task Calc = Task.Factory.StartNew (() => 7/x);
Try
{
Console.WriteLine (Calc. result);
}
catch (AggregateException AEX)
{
Console.Write (AEX.  Innerexception.message); Attempted to divide by 0
}
}
}
}

  

For a task with a parent-child relationship, an unhandled exception is passed to the parent task on a per-layer basis and finally wrapped in aggregateexception.

Using System;
Using System.Threading.Tasks;

Namespace Multithreadtest
{
Class Program
{
static void Main (string[] args)
{
TaskCreationOptions ATP = taskcreationoptions.attachedtoparent;
var parent = Task.Factory.StartNew (() =>
{
Task.Factory.StartNew (() =>//Child
{
Task.Factory.StartNew (() => {throw null;}, ATP); Grandchild
}, ATP);
});

The following call throws a NullReferenceException (wrapped
In nested aggregateexceptions):
Parent. Wait ();
}
}
}

  
Cancel Task

If you want to support cancellation, you need to pass in a cancellationtokensouce when you create a task

Sample code:

Using System;
Using System.Threading;
Using System.Threading.Tasks;

Namespace Multithreadtest
{
Class Program
{
static void Main (string[] args)
{
var cancelsource = new CancellationTokenSource ();
CancellationToken token = Cancelsource.token;

Task task = Task.Factory.StartNew (() =>
{
Do some stuff ...
Token.  Throwifcancellationrequested (); Check for cancellation request
Do some stuff ...
}, token);
Cancelsource.cancel ();

Try
{
Task. Wait ();
}
catch (AggregateException ex)
{
if (ex. InnerException is operationcanceledexception)
Console.Write ("Task canceled!");
}

Console.ReadLine ();
}
}
}

  
continuous execution of tasks


Continuations

Task scheduling is also a common requirement, and task supports the task of performing another task after the end of one.

Task Task1 = Task.Factory.StartNew (() => Console.Write ("antecedant.."));
Task Task2 = Task1. ContinueWith (Task =>console.write (".. Continuation "));

  
Continuations and Task

The task also has an overload with the return value, and the sample code is as follows:

Task.Factory.StartNew (() => 8)
. ContinueWith ant => Ant. Result * 2)
. ContinueWith ant => math.sqrt (ant. Result))
. ContinueWith ant => Console.WriteLine (Ant.   result)); Output 4

  
Child Tasks

As mentioned earlier, when you wait for a task, you also need to wait for its subtasks to complete.

The following code shows the task with the string:

Using System;
Using System.Threading.Tasks;
Using System.Threading;

Namespace Multithreadtest
{
Class Program
{
public static void Main (string[] args)
{
Task Parenttask = Task.Factory.StartNew (() =>
{
Int[] results = new INT[3];

Task T1 = new Task (() => {thread.sleep (3000); results[0] = 0;}, taskcreationoptions.attachedtoparent);
Task t2 = new Task (() => {thread.sleep (3000); results[1] = 1;}, taskcreationoptions.attachedtoparent);
task t3 = new Task (() => {thread.sleep (3000); results[2] = 2;}, taskcreationoptions.attachedtoparent);

T1. Start ();
T2. Start ();
T3. Start ();

return results;
});

Task Finaltask = parenttask.continuewith (parent =>
{
foreach (int result in parent.) Result)
{
Console.WriteLine (result);
}
});

Finaltask.wait ();
Console.ReadLine ();
}
}
}


The output of this code is: 1,2,3

Finaltask will wait until all subtasks have ended before executing.


taskfactory

about TaskFactory, in the example above we used the System.Threading.Tasks. Task.factory property to quickly create a task. Of course you can also create your own taskfactory, and you can specify your own taskcreationoptions,taskcontinuationoptions to make the task default behavior created by your factory different.

. NET has some default ways of creating tasks, because the default behavior of TaskFactory creating tasks may cause problems that are not easily discovered.

as in. NET 4.5, the Task joins a Run static method:

Task.run (someaction);

If you use this method instead of the Task.Factory.StartNew in the example above, you cannot get the correct result. The reason is that task.run create a task by default is to deny the addition of subtasks. The above code is equivalent to the following:

Task.Factory.StartNew (someaction, Cancellationtoken.none, Taskcreationoptions.denychildattach, Taskscheduler.default);

You can also create a taskfactory that has its own default behavior.


Regardless of threadpool or task, Microsoft is thinking of ways to implement thread reuse to save the overhead of creating destruction threads. Implementations within the thread pool may have different mechanisms in different versions. If possible, using a thread pool to manage threads is still a recommended choice.

 
We've mainly introduced the basics of task, and in our programming, there are scenarios where tasks can be used to improve the performance of a program that are often very similar, and Microsoft, in order to simplify programming, A series of parallel classes are encapsulated in the System.Threading.Tasks.Parallel, and the interior is implemented through tasks.


The For,foreach,invoke method of parallel


In the process of programming, we often use circular statements:

for (int i = 0; i < i++)
{
Dosomework (i);
}


If the work in the loop can be parallel, then we can use the following statement:

Parallel.For (0, I => dosomework (i));


We also often use foreach to traverse a collection:

foreach (var item in collection)
{
Dosomework (item);
}


If we use a thread pool to perform the tasks inside, then we can write:

Parallel.ForEach (collection, Item => dosomework (item));

Finally, if you want to execute several different methods in parallel, you can:

Parallel.Invoke (Method1, METHOD2, Method3);

  

If you look at the implementation in the background, you will find that the basic task is based on the thread pool, of course, you can also manually create a task set, and then wait for all the tasks to end to achieve the same function. The Parallel.For and Parallel.forach methods above do not assume that you can find all the for and foreach methods in your code and replace them, because each task assigns a delegate, and the online Cheng Chili executes, and if the task in the delegate is thread unsafe, you may also need to To lock to ensure thread safety, the use of lock itself can cause performance loss. If each task takes a long time to execute and is thread-safe, parallel will bring you a good performance boost. For short tasks, or thread insecure tasks, you need to weigh down whether you really need to use parallel.


NET Multithreading using Locks for synchronization


Synchronizing with Locks


Exclusive locks are used to ensure that only one thread can access a piece of code over a period of time. The two main types of exclusive locks are lock and mutex. Lock and mutex are more convenient to build and run faster. However, a mutex can be used by different processes on the same machine.

The lock keyword in


Monitor.Enter and Monitor.Exit

C # is actually a shorthand for monitor.enter,monitor.exist. In the. NET 1.0,2.0,3.0 version of C #, lock is compiled into the following code:
    
Monitor.Enter (_locker);
Try
{
    if (_val2!= 0) Console.WriteLine (_val1/_val2);
    _val2 = 0;
}
Finally {monitor.exit (_locker);}

If you call Monitor.Exit directly without calling Monitor.Enter, an exception is thrown.
Locktaken version:

Imagine the above code, and if you monitor.enter after it, the thread has an exception (such as being terminated) before the try, in which case the finally exit method will never be executed. It also leads to the lock not being released. To avoid this situation, the designer of the CLR 4.0 overloads the Monitor.Enter method:

public static void Enter (object obj, ref bool Locktaken), and

If the current thread causes the lock to not be fetched because of some exceptions, the Locktake value is false, so in CLR 4.0, lock is interpreted as follows:

bool locktaken = false;
Try
{
 
    monitor.enter (_locker, ref locktaken);
&NBSP
   //do your stuff ...
}
 
finally {if (locktaken) monitor.exit (_locker);}


TryEnter

Monitor also provides a TryEnter method that allows you to set a time-out period to prevent the current thread from acquiring a lock for a long time and waiting.


Select the correct synchronization object

You need to select a lock (obj) object that is visible to all threads to ensure that the program executes as you intended. If you don't understand some of the features in the C # language, lock may not perform as you expect.

    Lock ("string") is not a good choice because of the host mechanism of the string
    lock A value type is not a good choice
     Lock (typeof (..)) Not a good choice, because the System.Type feature

when to use lock

is a basic rule, you need to lock any write or modifiable field. Even if it is an assignment operation, or an additive operation, you cannot assume that he is thread-safe.

For example, the following code is not thread safe:

Class Threadunsafe
{
    static int _x;
    static void Increment () {_x++;}
    static void Assign () {_x = 123;}
}

 

You need to write this:

class Threadsafe
{
    static ReadOnly Object _ Locker = new Object ();
    static int _x;
 
    static void Increment () {lock (_locker) _x++;}
    static void Assign () {lock (_locker) _x = 123;}
}

If you look at the implementation in some BCL class libraries, you can see that in some cases the interlocked class is used instead of lock, which we'll introduce later.


about nested locks or reentrant

When you read some documents, some documents may say lock or Monitor.Enter is reentrant (reentrant), so how do we understand reentrant?

Imagine the following code:

Lock (Locker)
Lock (Locker)
Lock (Locker)
{
Do something ...
}



Or is:

Monitor.Enter (Locker); Monitor.Enter (Locker); Monitor.Enter (Locker);
Do something ...
Monitor.Exit (Locker); Monitor.Exit (Locker); Monitor.Exit (Locker);

In this case, the locker is only available if the last exit is executed, or after the corresponding number of exit is executed.
Mutex

Mutexes are like lock in C #, but can still be used in different processes. In other words, a mutex is a computer-level lock. So getting a lock like this is a lot slower than monitor.

Sample code:

Using System;
Using System.Threading.Tasks;
Using System.Threading;

Namespace Multithreadtest
{
Class Oneatatimeplease
{
static void Main ()
{
Naming a Mutex makes it available computer-wide. Use a name of that ' s
Unique to your company and application (e.g., include your URL).

using (var mutex = new Mutex (false, "oreilly.com Oneatatimedemo"))
{
Wait a few seconds if contended, in case another instance
The program is still in the process of shutting down.

if (!mutex. WaitOne (Timespan.fromseconds (3), false)
{
Console.WriteLine ("Another app instance is running. Bye! ");
Return
}
Runprogram ();
}
}

static void Runprogram ()
{
Console.WriteLine ("Running.") Press Enter to exit ");
Console.ReadLine ();
}
}
}


Semaphore

Both monitor and mutex are exclusive locks that semaphore another type of non-exclusive lock that we often use.

We use it to achieve an example: a bar that can hold up to 3 people, if a full time to wait, guests leave, waiting for people to come in anytime.

Sample code:

Using System;
Using System.Threading;

Class Theclub//No door lists!
{
Static semaphore _sem = new Semaphore (3, 3); Capacity of 3

static void Main ()
{
for (int i = 1; I <= 5; i++) New Thread (Enter). Start (i);

Console.ReadLine ();
}

static void Enter (object ID)
{
Console.WriteLine (id + "wants to enter");
_sem. WaitOne ();
Console.WriteLine (id + "is in!"); Only three threads
Thread.Sleep (1000 * (int) ID); Can is here
Console.WriteLine (id + "is leaving"); A time.
_sem. Release ();
}
}



Using semaphore requires callers to control access to resources, call WaitOne to obtain resources, and release resources by releasing them. Developers have a responsibility to ensure that resources are released correctly.

Semaphore is useful when restricting synchronous access, not like a monitor or a mutex, when one thread accesses certain resources, all other threads need to wait, and instead sets a buffer that allows up to a maximum number of threads to access at the same time.

Semaphore can also be synchronized across processes like a mutex.

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.