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();
        }
    }

上述代码中就是利用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();
        }
 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("递减线程结束");
        }
    }

你可能感兴趣的:(c#)