This article is primarily a sample code and test results for implementing multi-process thread synchronization (process synchronization) at the operating system level. The code has been tested for reference and can be used directly.
A business scenario for a previous blog [C # using read-write locks three lines of code to easily solve the problem of thread synchronization when multiple threads concurrently write files].
With the increase of the service process, the thread synchronization within the process cannot satisfy the present demand , which causes the problem that the file is occupied when the multi-process writes the same file simultaneously.
In this scenario, the lock across the process level is unavoidable. In the reference provided by. NET, the process lock inherits the System.Threading.WaitHandle class .
TheSystem.Threading.Mutex class is undoubtedly the simplest and most appropriate choice for scenarios in this article that allow only a single process (thread) to operate on a single file at the same time.
Objects of this type can use a named (string) mutex to achieve the current session-level or operating-system-level synchronization requirements . I chose the synchronous authoring example at the operating system level because of the broader coverage.
Here is the implementation code, the comments are not detailed in detail:
namespacewaithandleexample{classProgram {Static voidMain (string[] args) { #regionSimple to use//var mutexkey = mutexexample.getfilepathmutexkey ("file path"); //mutexexample.mutexexec (Mutexkey, () =//{ //Console.WriteLine ("Code required for process synchronization execution"); //}); #endregion #regionTest codevarFilePath = Path.Combine (AppDomain.CurrentDomain.BaseDirectory,"Test.log"). ToUpper (); varMutexkey =Mutexexample.getfilepathmutexkey (FilePath); //Open N Write threads at a timeParallel.For (0, Logcount, E = { //no mutex operations are written, lots of write errors, FileStream contains FileShare constructors also implement thread synchronization in-process, and errors occur when multiple processes are written simultaneously//Writelog (FilePath); //write with mutex operation, no error due to only one thread operation at the same timeMutexexample.mutexexec (Mutexkey, () ={writelog (FilePath); }); }); Console.WriteLine (string. Format ("Log count:{0}.\t\twrited count:{1}.\tfailed count:{2}.", Logcount.tostring (), writedcount.tostring (), failedcount.tostring ()); Console.read (); #endregion } /// <summary> ///C # mutexes use sample code/// </summary> /// <remarks>tested and run on-line for direct use</remarks> Public Static classMutexexample {/// <summary> ///A simple example of synchronous execution between processes/// </summary> /// <param name= "Action" >Synchronous Processing Code</param> /// <param name= "Mutexkey" >synchronization keys at the operating system level///(if name is specified as null or an empty string, a local mutex is created.) ///If the name begins with the prefix "global\", the mutex is visible in all Terminal Server sessions. ///If the name begins with the prefix "local\", the mutex is only visible in the Terminal Server session in which it was created. ///If you do not specify a prefix when you create a named mutex, it takes the prefix "local\". )</param> /// <remarks>A simple example that does not retry and does not consider exception handling</remarks>[Obsolete (Error:false, Message:"Please use mutexexec")] Public Static voidMutexexeceasy (stringMutexkey, Action action) { //declares a named mutex, implements inter-process synchronization, the named mutex does not exist, is created automatically, and is available directly using(Mutex Mut =NewMutex (false, Mutexkey)) { Try { //locked, other threads wait for the lock to be released before processing can be performed, and if other threads are locked or prioritized, wait for the other thread to finishMut. WaitOne (); //Execute the processing code (in the time period called WaitHandle.WaitOne to Waithandle.releasemutex, only one thread is processed, and the other thread waits for the lock to be released before the code snippet can be executed)action (); } finally { //release the lock so that other processes (or threads) can continue to executeMut. ReleaseMutex (); } } } /// <summary> ///get the Process sync key for the file name/// </summary> /// <param name= "FilePath" >file path (note case and space)</param> /// <returns>process synchronization key (mutex name)</returns> Public Static stringGetfilepathmutexkey (stringFilePath) { //the synchronization key for the generated file, customizable format (the mutex name is unfriendly to the special character support, and then to the BASE64 format string) varFilekey = convert.tobase64string (Encoding.Default.GetBytes (string. Format (@"file\{0}", FilePath))); //convert to OS-level sync key varMutexkey =string. Format (@"global\{0}", Filekey); returnMutexkey; } /// <summary> ///synchronous execution between processes/// </summary> /// <param name= "Mutexkey" >synchronization keys at the operating system level///(if name is specified as null or an empty string, a local mutex is created.) ///If the name begins with the prefix "global\", the mutex is visible in all Terminal Server sessions. ///If the name begins with the prefix "local\", the mutex is only visible in the Terminal Server session in which it was created. ///If you do not specify a prefix when you create a named mutex, it takes the prefix "local\". )</param> /// <param name= "Action" >Synchronous processing Operations</param> Public Static voidMutexexec (stringMutexkey, Action action) {mutexexec (Mutexkey:mutexkey, Action:action, recursive:false); } /// <summary> ///synchronous execution between processes/// </summary> /// <param name= "Mutexkey" >synchronization keys at the operating system level///(if name is specified as null or an empty string, a local mutex is created.) ///If the name begins with the prefix "global\", the mutex is visible in all Terminal Server sessions. ///If the name begins with the prefix "local\", the mutex is only visible in the Terminal Server session in which it was created. ///If you do not specify a prefix when you create a named mutex, it takes the prefix "local\". )</param> /// <param name= "Action" >Synchronous processing Operations</param> /// <param name= "Recursive" >Indicates whether the current call is recursive, throws an exception when recursive processing detects an exception, and avoids entering infinite recursion</param> Private Static voidMutexexec (stringMutexkey, Action Action,BOOLrecursive) { //declares a named mutex, implements inter-process synchronization, the named mutex does not exist, is created automatically, and is available directly//Initiallyowned:false: The default current thread does not have ownership of an existing mutex, that is, the default thread is not the first to create the named mutex//Note: When a named mutex with the same name is declared concurrently, if the interval is too short, multiple mutexes with the same name may be declared, and multiple mutexes with the same names are not synchronized, and high concurrent users are treated separately using(Mutex Mut =NewMutex (initiallyowned:false, Name:mutexkey)) { Try { //locked, other threads wait for the lock to be released before processing can be performed, and if other threads are locked or prioritized, wait for the other thread to finishMut. WaitOne (); //Execute the processing code (in the time period called WaitHandle.WaitOne to Waithandle.releasemutex, only one thread is processed, and the other thread waits for the lock to be released before the code snippet can be executed)action (); } //A Abandonedmutexexception exception is thrown when another process is locked and no mutex is normally released (for example, the process suddenly shuts down or exits) Catch(Abandonedmutexexception ex) {//avoid entering infinite recursion if(Recursive)Throwex; //non-recursive invocation, when a mutex lock exception is thrown by another process, retry executionMutexexec (Mutexkey:mutexkey, Action:action, recursive:true); } finally { //release the lock so that other processes (or threads) can continue to executeMut. ReleaseMutex (); } } } } #regionTest the code that writes the fileStatic intLogcount = -; Static intWritedcount =0; Static intFailedcount =0; Static voidWritelog (stringLogFilePath) { Try { varnow =DateTime.Now; varLogcontent =string. Format ("Tid: {0}{1} {2}. {3}\r\n", Thread.CurrentThread.ManagedThreadId.ToString (). PadRight (4), now. Tolongdatestring (), now. Tolongtimestring (), now.Millisecond.ToString ()); File.appendalltext (LogFilePath, logcontent); Writedcount++; } Catch(Exception ex) {Console.WriteLine (ex). Message); Failedcount++; } } #endregion }}
test does not use process synchronization, multi-process multithreading simultaneously write files:
Test Result:6 processes make 3,000 write requests at the same time, only 277 successful writes
test using mutex for process synchronization, multi-process multithreading simultaneously writing files:
Test Result:6 processes make 3,000 write requests at the same time, all successfully write
Add:
The resource consumption and efficiency of the process synchronization is much worse than the thread synchronization, please use it reasonably according to the actual scenario.
Although this document is written as an example, the code usage scenarios for process synchronization are not related to file operations.
Although the Semaphore Class (beacon) can limit the number of simultaneous threads, or even set the maximum simultaneous operand to 1 o'clock, the behavior is similar to the mutex class (mutex), but because the semaphore does not receive an exception notification when an exception exits in another process, the exception can only be triggered by waiting for a timeout. is not suitable for the present scene, so does not tell.
For additional in-depth understanding and application of process synchronization, see additional information.
C # uses mutexes (mutexes) to implement multi-proc thread synchronization operations (process synchronization) When multi-process concurrency operations