c#Mutex的用法總結


c#Mutex的用法總結

msdn中對於Mutex的解釋是:可用於進程間同步的同步基元,顧名思義也就是可用於進程中的同步,並且c#本質論中也提出了Mutex可以用於同步對文件或者其它跨進程資源的訪問,下面就有幾個疑問?
1、Mutex能不能用於同一進程下的不同線程的同步?
答:Mutex可以用於同一進程下不同線程的同步。
1)

 private static int count=0; public Form1() { InitializeComponent(); Task task1 = Task.Run(()=> Increase()); Task task2 = Task.Run(()=>Decrease()); Task.WaitAll (new Task[] { task1,task2}); Console.WriteLine(count); } private static void Increase() { for(int i=0;i<int.MaxValue;i++) { count++; } } private static void Decrease() { for(int i=0;i<int.MaxValue;i++) { count--; } } 

 

最終控制台輸出的結果是-456690760,而非我們想象的0,這就是由於多個線程訪問相同的數據造成了競態條件導致,下面我們使用Mutex來實現對count變量的同步,如下:

  private static int count = 0; Mutex mutex = new Mutex(false ); public Form1() { InitializeComponent(); Task task1 = Task.Run(() => Increase()); Task task2 = Task.Run(() => Decrease()); Task.WaitAll(new Task[] { task1, task2 }); Console.WriteLine(count); } private void Increase() { mutex.WaitOne();//申請互斥體 for (int i = 0; i < int.MaxValue; i++) { count++; } mutex.ReleaseMutex(); } private void Decrease() { mutex.WaitOne(); for (int i = 0; i < int.MaxValue; i++) { count--; } mutex.ReleaseMutex(); } 

2、Mutex在跨進程中怎么使用?
下面舉兩個例子說明Mutex在跨進程中的使用;
1)防止應用程序被打開兩次;
static void Main()
{
bool createNew;
Mutex mt = new Mutex(true, “ApplicationMutex”, out createNew);

        if (createNew)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new Form1());
        }
        else
        {
            MessageBox.Show("程序已經打開!");
            Process.GetCurrentProcess().Kill();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

上述代碼中就是利用Mutex構造函數的第三個參數來防止程序被多次打開,Msdn的解釋是:
如果創建了局部互斥體(即,如果 name 為 null 或空字符串)或指定的命名系統互斥體,則createdNew 返回true;如果指定的命名系統互斥體已存在,則為 createdNew 返回false。
2)Mutex在多個進程中訪問文件時實現同步;
多進程不同步對文件的訪問時:
System.IO.IOException:“文件“d:\TestFile\1.txt”正由另一進程使用,因此該進程無法訪問此文件。”
進程一如下:

 public partial class Form1 : Form { Mutex mutex = new Mutex(false, "MyMutex"); public Form1() { InitializeComponent(); Thread thread = new Thread(WriteFile); thread.IsBackground = true; thread.Start("d:\\TestFile\\1.txt"); } private void WriteFile(object fileName) { while (true) { string TempfileName = fileName.ToString(); //mutex.WaitOne(); StreamWriter sw = new StreamWriter(TempfileName, false, Encoding.GetEncoding("GB2312")); sw.WriteLine(DateTime.Now.ToString()); sw.Flush(); sw.Close(); //mutex.ReleaseMutex(); Thread.Sleep(10); } } } 

進程二如下:

  public partial class Form1 : Form { Mutex mutex = new Mutex(false, "MyMutex"); public Form1() { InitializeComponent(); Thread thread = new Thread(ReadFile); thread.IsBackground = true; thread.Start("d:\\TestFile\\1.txt"); } private void ReadFile(object fileName) { while(true) { string tempFileName = fileName.ToString(); string str; //mutex.WaitOne(); StreamReader sr = new StreamReader(tempFileName, Encoding.GetEncoding("GB2312")); str = sr.ReadLine(); Thread.Sleep(10); sr.Close(); //mutex.ReleaseMutex(); this.Invoke(new Action (() => label1.Text = str)); } } } 

同時運行這個兩個進程后,就會報出文件被另一進程占用的異常。
多進程同步對文件的訪問:
進程一如下:

  public partial class Form1 : Form { Mutex mutex = new Mutex(false, "MyMutex"); public Form1() { InitializeComponent(); Thread thread = new Thread(WriteFile); thread.IsBackground = true; thread.Start("d:\\TestFile\\1.txt"); } private void WriteFile(object fileName) { while (true) { string TempfileName = fileName.ToString(); mutex.WaitOne(); StreamWriter sw = new StreamWriter(TempfileName, false, Encoding.GetEncoding("GB2312")); sw.WriteLine(DateTime.Now.ToString()); sw.Flush(); sw.Close(); mutex.ReleaseMutex(); Thread.Sleep(10); } } } 

進程二如下:

  public partial class Form1 : Form { Mutex mutex = new Mutex(false, "MyMutex"); public Form1() { InitializeComponent(); Thread thread = new Thread(ReadFile); thread.IsBackground = true; thread.Start("d:\\TestFile\\1.txt"); } private void ReadFile(object fileName) { while (true) { string tempFileName = fileName.ToString(); string str; mutex.WaitOne(); StreamReader sr = new StreamReader(tempFileName, Encoding.GetEncoding("GB2312")); str = sr.ReadLine(); Thread.Sleep(10); sr.Close(); mutex.ReleaseMutex(); this.Invoke(new Action(() => label1.Text = str)); } } } 

3、常見錯誤及原因分析
1)System.ApplicationException:“從不同步的代碼塊中調用了對象同步方法

 private void Decrease() { for (int i = 0; i < int.MaxValue; i++) { count--; } mutex.ReleaseMutex(); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
 private static int count = 0; Mutex mutex = new Mutex(true); public Form1() { InitializeComponent(); Task task2 = Task.Run(() => Decrease()); Thread.Sleep(10000); //Task task1 = Task.Run(() => Increase()); //Task.WaitAll(new Task[] { task1, task2 }); Console.WriteLine(count); } 

上面的代碼就會報這個錯誤,這是由於Mutex創建對象時,第一個參數為true, Mutex mutex = new Mutex(true);這個true的意思是讓創建這個同步對象的線程擁有互斥體,這里創建互斥體的線程是UI線程,但是我們在Task2中調用了mutex.ReleaseMutex()這個同步方法,而Mutex使用時就有一個規定擁有互斥體的線程才可以去釋放互斥體,所以出現了上面的錯誤,如果將代碼改成下面這兩種情況,則就不會出問題。
情況一、直接在UI線程調用ReleaseMutex

public Form1() { InitializeComponent(); Decrease(); //Task task2 = Task.Run(() => Decrease()); Thread.Sleep(10000); //Task task1 = Task.Run(() => Increase()); //Task.WaitAll(new Task[] { task1, task2 }); Console.WriteLine(count); } 

情況二、在task2中創建Mutex的對象

    public partial class Form1 : Form { private static int count = 0; public Form1() { InitializeComponent(); Task task2 = Task.Run(() => Decrease()); Thread.Sleep(10000); //Task task1 = Task.Run(() => Increase()); //Task.WaitAll(new Task[] { task1, task2 }); Console.WriteLine(count); } //private void Increase() //{ // mutex.WaitOne(); // for (int i = 0; i < int.MaxValue; i++) // { // count++; // } // mutex.ReleaseMutex(); //} private void Decrease() { Mutex mutex = new Mutex(true); for (int i = 0; i < int.MaxValue; i++) { count--; } mutex.ReleaseMutex(); } } 

2)System.Threading.AbandonedMutexException:“由於出現被放棄的 mutex,等待過程結束。”
msdn中指出,如果一個線程終止時擁有互斥體,則認為該互斥量將放棄。 互斥體的狀態將設置為終止狀態,並等待下一步的線程獲取所有權。也就是說線程程序執行完畢,但是線程用的Mutex對象沒有release,如果這時再次申請互斥體,則認為互斥體的代碼已經執行完畢了,申請互斥體的線程可以獲取互斥體,但是這里有一個線程申請互斥體時間的問題,如果擁有互斥體的線程1中的代碼沒有執行完畢,線程2調用waitone申請互斥體,那么當線程1代碼執行完畢,但是沒有release掉mutex的話,則線程2就會報System.Threading.AbandonedMutexException這個錯誤,所以我們在使用中一定要注意釋放Mutex的實例,報錯代碼如下:

public partial class Form1 : Form { private static int count = 0; Mutex mutex; public Form1() { InitializeComponent(); Task task2 = Task.Run(() => Decrease()); Task task1 = Task.Run(() => Increase()); Task.WaitAll(new Task[] { task1, task2 }); Console.WriteLine(count); } private void Increase() { mutex.WaitOne(); for (int i = 0; i < int.MaxValue; i++) { count++; } mutex.ReleaseMutex(); Console.WriteLine("遞增線程結束"); } private void Decrease() { mutex = new Mutex(true); for (int i = 0; i < int.MaxValue; i++) { count--; } //mutex.ReleaseMutex(); Console.WriteLine("遞減線程結束"); } } 

如果上述代碼中線程task1在task2執行完畢后再去申請互斥體,則不會報錯,代碼如下:

 public partial class Form1 : Form { private static int count = 0; Mutex mutex; public Form1() { InitializeComponent(); Task task2 = Task.Run(() => Decrease()); Thread.Sleep(10000); Task task1 = Task.Run(() => Increase()); Task.WaitAll(new Task[] { task1, task2 }); Console.WriteLine(count); } private void Increase() { mutex.WaitOne(); for (int i = 0; i < int.MaxValue; i++) { count++; } mutex.ReleaseMutex(); Console.WriteLine("遞增線程結束"); } private void Decrease() { mutex = new Mutex(true); for (int i = 0; i < int.MaxValue; i++) { count--; } //mutex.ReleaseMutex(); Console.WriteLine("遞減線程結束"); } }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM