Home  >  Article  >  Backend Development  >  Detailed introduction to graphic code examples of C# thread synchronization

Detailed introduction to graphic code examples of C# thread synchronization

黄舟
黄舟Original
2017-03-08 11:14:211277browse

This article mainly introduces the relevant knowledge of thread synchronization in C#. It has a very good reference value. Let’s take a look at it with the editor below

Foreword

When a thread in the thread pool is blocked, the thread pool will create additional threads, and creating, destroying and scheduling threads is quite expensive. memory resources. In addition, many developers are used to creating more threads when they see that the threads of their programs are not doing anything useful. In order to build scalable and responsive programs, we introduced a detailed explanation of C# asynchronous programming earlier

But asynchronous programming also has serious problems. If two different threads access the same variables and data, according to the implementation of our asynchronous function, it is impossible for two threads to access the same data at the same time. At this time we need Thread synchronization. When multiple threads access shared data at the same time, thread synchronization can prevent data corruption. The reason why the concept of simultaneousness is emphasized is because the essence of thread synchronization is a timing issue.

Asynchronous and synchronous are relative. Synchronization means sequential execution. After executing one, you need to wait and coordinate the execution of the next one. Asynchronous means that they are independent of each other and continue to do their own things while waiting for an event. There is no need to wait for the event to complete before working again. Threads are a way to achieve asynchronous. Asynchronous means that the main thread that calls the method does not need to wait for the completion of another thread synchronously, so that the main thread can do other things.

Primitives User Mode and Kernel Mode Constructs

Basic Concepts

Primitives: simple constructs that can be used in code

User Mode: Threads are coordinated through special CPU instructions, the operating system always Undetected A thread blocking on a primitive user-mode construct.

Kernel mode: Provided by Windows itself, functions implemented by the kernel are called in the thread of the application.

User mode constructs

Volatile constructs

The C# compiler, JIT compiler and CPU will all optimize the code. They try to ensure that our intentions are preserved, but from a multi-threading perspective, we The intention will not necessarily be preserved. Here is an example:

 static void Main(string[] args)
 {
 Console.WriteLine("让worker函数运行5s后停止");
 var t = new Thread(Worker);
 t.Start();
 Thread.Sleep(5000);
 stop = true;
 Console.ReadLine();
 }
 private static bool stop = false;
 private static void Worker(object obj)
 {
 int x = 0;
 while (!stop)
 {
 x++;
 }
 Console.WriteLine("worker函数停止x={0}",x);
 }

If the compiler checks that stop is false, it generates code to enter an infinite loop and keeps incrementing x in the loop, so the loop is optimized It is completed quickly, but the compiler only detects stop once, not every time.

Example 2---Two threads access simultaneously:

class test
 {
 private static int m_flag = 0;
 private static int m_value = 0;
 public static void Thread1(object obj)
 {
 m_value = 5;
 m_flag = 1;
 }
 public static void Thread2(object obj)
 {
 if (m_flag == 1)
 Console.WriteLine("m_value = {0}", m_value);
 }
 //多核CPU机器才会出现线程同步问题
 public void Exec()
 {
 var thread1 = new Thread(Thread1);
 var thread2 = new Thread(Thread2);
 thread1.Start();
 thread2.Start();
 Console.ReadLine();
 }
 }

When the program is executed, the compiler must read the variables m_flag and m_value from RAM into the CPU register. RAM first passes the value of m_value 0 to thread1 Change the value to 5, but thread2 does not know that thread2 still thinks the value is 0. Generally speaking, this problem is more likely to occur on multi-core CPUs. The more CPUs there are, the greater the chance that multiple threads will access resources at the same time.

The keyword volatile disables some optimizations performed by the C# compiler, JTP compiler and CPU. If applied to variables, the field will not be allowed to be cached in the CPU's register, ensuring that the reading and writing of fields are performed in RAM. .

Interlocking construction

Each method in the System.Threading.Interlocked class performs an atomic read and write operation. Any variable write before calling an Interlocked method will precede the call of this Interlocked method. is executed, and any variable reads after the call are read after this call.

Interlocked method mainly performs static operations on INT32 variables such as Add, Decrement, Compare, Exchange, CompareChange and other methods, and also accepts parameters of object, Double and other types.

Atomic operation: refers to an operation that will not be interrupted by the thread scheduling mechanism; once this operation starts, it will run until the end without any context switch (switching to another thread) in the middle.

Code demonstration:

Instructions: Query several web servers asynchronously through the Interlocked method and return data at the same time, and the results are only executed once.

//上报状态类型
 enum CoordinationStatus
 {
 Cancel,
 Timeout,
 AllDone
 }

class AsyncCoordinator
 {
 //AllBegun 内部调用JustEnded来递减它
 private int _mOpCount = 1;
 //0=false,1=true
 private int _mStatusReported = 0;
 private Action<CoordinationStatus> _mCallback;
 private Timer _mTimer;
 //发起一个操作之前调用
 public void AboutToBegin(int opsToAdd = 1)
 {
 Interlocked.Add(ref _mOpCount, opsToAdd);
 }
 //处理好一个操作的结果之后调用
 public void JustEnded()
 {
 if (Interlocked.Decrement(ref _mOpCount) == 0)
 {
 ReportStatus(CoordinationStatus.AllDone);
 } 
 }
 //该方法必须在发起所有操作后调用
 public void AllBegin(Action<CoordinationStatus> callback, int timeout = Timeout.Infinite)
 {
 _mCallback = callback;
 if (timeout != Timeout.Infinite)
 {
 _mTimer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);
 JustEnded();
 }
 }
 private void TimeExpired(object o)
 {
 ReportStatus(CoordinationStatus.Timeout);
 }
 public void Cancel()
 {
 ReportStatus(CoordinationStatus.Cancel);
 }
 private void ReportStatus(CoordinationStatus status)
 {
 //如果状态从未报告过,就报告它,否则就忽略它,只调用一次
 if (Interlocked.Exchange(ref _mStatusReported, 1) == 0)
 {
 _mCallback(status);
 } 
 }
 }

class MultiWebRequest
 {
 //辅助类 用于协调所有的异步操作
 private AsyncCoordinator _mac = new AsyncCoordinator();
 protected Dictionary<string,object> _mServers = new Dictionary<string, object>
 {
 {"http://www.baidu.com",null},{"http://www.Microsoft.com",null},{"http://www.cctv.com",null},
 {"http://www.souhu.com",null},{"http://www.sina.com",null},{"http://www.tencent.com",null},
 {"http://www.youku.com",null}
 };
 private Stopwatch sp;
 public MultiWebRequest(int timeout = Timeout.Infinite)
 {
 sp = new Stopwatch();
 sp.Start();
 //通过异步方式一次性发起请求
 var httpclient = new HttpClient();
 foreach (var server in _mServers.Keys)
 {
 _mac.AboutToBegin(1);
 httpclient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task));
 }
 _mac.AllBegin(AllDone,timeout);
 Console.WriteLine("");
 }
 private void ComputeResult(string server, Task<Byte[]> task)
 {
 object result;
 if (task.Exception != null)
 {
 result = task.Exception.InnerException;
 }
 else
 {
 //线程池处理IO
 result = task.Result.Length;
 }
 //保存返回结果的长度
 _mServers[server] = result;
 _mac.JustEnded();
 }
 public void Cancel()
 {
 _mac.Cancel();
 }
 private void AllDone(CoordinationStatus status)
 {
 sp.Stop();
 Console.WriteLine("响应耗时总计{0}",sp.Elapsed);
 switch (status)
 {
 case CoordinationStatus.Cancel:
  Console.WriteLine("操作取消");
  break;
 case CoordinationStatus.AllDone:
  Console.WriteLine("操作完成,完成的结果如下");
  foreach (var server in _mServers)
  {
  Console.WriteLine("{0}",server.Key);
  object result = server.Value;
  if (result is Exception)
  {
  Console.WriteLine("错误原因{0}",result.GetType().Name);
  }
  else
  {
  Console.WriteLine("返回字节数为:{0}",result);
  }
  }
  break;
 case CoordinationStatus.Timeout:
  Console.WriteLine("操作超时");
  break;
 default:
  throw new ArgumentOutOfRangeException("status", status, null);
 }
 }
 }

It is highly recommended that you refer to the above code. I will often refer to this model when accessing the server.

Simple spin lock

class SomeResource
 {
 private SimpleSpinLock s1 = new SimpleSpinLock();
 public void AccessResource()
 {
 s1.Enter();
 //一次是有一个线程才能进入访问
 s1.Leave();
 }
 }
 class SimpleSpinLock
 {
 private int _mResourceInUse;
 public void Enter()
 {
 while (true)
 {
 if(Interlocked.Exchange(ref _mResourceInUse,1)==0)
  return;
 }
 }
 public void Leave()
 {
 Volatile.Write(ref _mResourceInUse,1);
 }
 }

This is a simple implementation of a thread synchronization lock. The biggest problem with this kind of lock is that when there is competition, it will cause the thread to "spin", which will It wastes valuable CPU time and prevents the CPU from doing more work. Therefore, this spin lock should be used to protect code that executes very quickly.


The above is the detailed content of Detailed introduction to graphic code examples of C# thread synchronization. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn