C # restrictions on concurrent programming concurrent access to resources using SemaphoreSlim
Summary
When multiple tasks or threads run in parallel, it is difficult to avoid concurrent access to some limited resources. You can consider using semaphores for this control (System. Threading. Semaphore) is a Semaphore object representing a Windows kernel. If the expected wait time is short, you can consider using SemaphoreSlim, which causes less overhead.
Semaphores in. NetFrameWork coordinate resource access by tracking the tasks or threads that enter and exit. The semaphore needs to know the maximum number of resources. When a task enters, the resource counter will be reduced by 1. When the counter is 0, if a task accesses the resource, it will be blocked, until a task leaves.
Example program: 10 tasks access 3 resources in parallel
using System;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Diagnostics;namespace Sample5_8_semaphoreslim{ class Program { private static int _TaskNum = 10; private static Task[] _Tasks; private const int MAX_RESOURCE = 3; private const int RUN_LOOP = 10; private static SemaphoreSlim m_Semaphore; private static void Work1(int TaskID) { int i = 0; var sw = Stopwatch.StartNew(); var rnd = new Random(); while (i < RUN_LOOP) { Thread.Sleep(rnd.Next(200, 500)); Console.WriteLine("TASK " + TaskID + " REQUESTing {"); m_Semaphore.Wait(); try { Console.WriteLine("TASK " + TaskID + " WOrking ... ..." + i); sw.Restart(); Thread.Sleep(rnd.Next(200, 500)); } finally { Console.WriteLine("TASK " + TaskID + " REQUESTing }"); m_Semaphore.Release(); i++; } } } static void Main(string[] args) { _Tasks = new Task[_TaskNum]; m_Semaphore = new SemaphoreSlim(MAX_RESOURCE); int i = 0; for (i = 0; i < _TaskNum; i++) { _Tasks[i] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work1(taskid); }, i); } var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) => { Task.WaitAll(_Tasks); Console.WriteLine("=========================================================="); Console.WriteLine("All Phase is completed"); Console.WriteLine("=========================================================="); }); try { finalTask.Wait(); } catch (AggregateException aex) { Console.WriteLine("Task failed And Canceled" + aex.ToString()); } finally { m_Semaphore.Dispose(); } Console.ReadLine(); } }}
Timeout and Cancellation
Of course, semaphores cannot be blocked permanently. Semaphores also provide a timeout processing mechanism. The method is to input a TIMEOUT Wait time-Wait (int TIMEOUT) in the Wait function ). When the Wait return value is false, it indicates that it has timed out. If-1 is input, it indicates an indefinite wait.
Program example: note that m_Semaphore.Release (); has been commented out, and the task will wait 1 second and then time out.
using System;using System.Text;using System.Threading;using System.Threading.Tasks;using System.Diagnostics;namespace Sample5_8_semaphoreslim{ class Program { private static int _TaskNum = 10; private static Task[] _Tasks; private const int MAX_RESOURCE = 3; private const int RUN_LOOP = 10; private static SemaphoreSlim m_Semaphore; private static void Work1(int TaskID) { int i = 0; var sw = Stopwatch.StartNew(); var rnd = new Random(); while (i < RUN_LOOP) { Thread.Sleep(rnd.Next(200, 500)); Console.WriteLine("TASK " + TaskID + " REQUESTing {"); if (!m_Semaphore.Wait(1000)) { Console.WriteLine("TASK " + TaskID + " TIMEOUT!!!"); return; } try { Console.WriteLine("TASK " + TaskID + " WOrking ... ..." + i); sw.Restart(); Thread.Sleep(rnd.Next(2000, 5000)); } finally { Console.WriteLine("TASK " + TaskID + " REQUESTing }"); //m_Semaphore.Release(); i++; } } } static void Main(string[] args) { _Tasks = new Task[_TaskNum]; m_Semaphore = new SemaphoreSlim(MAX_RESOURCE); int i = 0; for (i = 0; i < _TaskNum; i++) { _Tasks[i] = Task.Factory.StartNew((num) => { var taskid = (int)num; Work1(taskid); }, i); } var finalTask = Task.Factory.ContinueWhenAll(_Tasks, (tasks) => { Task.WaitAll(_Tasks); Console.WriteLine("=========================================================="); Console.WriteLine("All Phase is completed"); Console.WriteLine("=========================================================="); }); try { finalTask.Wait(); } catch (AggregateException aex) { Console.WriteLine("Task failed And Canceled" + aex.ToString()); } finally { m_Semaphore.Dispose(); } Console.ReadLine(); } }}
Cross-process or AppDomain Synchronization
Semaphore can be used if cross-process or AppDomain synchronization is required. Semaphore is the Windows Kernel Semaphore, so it is effective throughout the system.
Its main interfaces are Release and WaitOne, which are used in the same way as SemaphoreSlim.