为什么我的代码无法在同步中正常工作?

时间:2020-07-28 12:22:36

标签: c# multithreading thread-safety mutex

我正在使用此类来防止两个应用程序(一个Windows应用程序和一个在同一OS上运行的Web应用程序)同时使用共享文件。但是我收到错误消息“从未同步的代码块调用了对象同步方法。”

    class SharedMutex : IDisposable
    {
        readonly Mutex mutex;

        /// <summary>
        /// This function will wait if other thread has owned the mutex
        /// </summary>
        /// <param name="name"></param>
        public SharedMutex(string name)
        {
            bool m = Mutex.TryOpenExisting(name, out mutex);
            if (m)
            {
                mutex.WaitOne();
            }
            else
            {
                mutex = new Mutex(true, name);
            }
        }

        public const string Logs = @"Global\Logs";

        public void Dispose()
        {
            mutex.ReleaseMutex();
            mutex.Dispose();
        }
    }

这就是我使用此类的方式

using (new SharedMutex(SharedMutex.Logs))
                {
                   ///access the shared file
                }

两个项目中都存在此类。

注意:我不是在寻找访问文件问题的解决方案,我需要知道为什么我的代码有问题。因为我也想将此代码用于其他目的。 谢谢。

1 个答案:

答案 0 :(得分:2)

我认为这很可能是由种族状况引起的(如问题注释中的/ u / Sinatr建议)。

以下代码重现了该问题:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            var t1 = Task.Run(func1);
            var t2 = Task.Run(func2);

            Task.WaitAll(t1, t2);

            Console.WriteLine("Done. Press <ENTER>");
            Console.ReadLine();
        }

        static void func1()
        {
            using (new SharedMutex("test"))
            {
                Thread.Sleep(2000);
            }
        }

        static void func2()
        {
            using (new SharedMutex("test"))
            {
                Thread.Sleep(1000);
            }
        }
    }

    class SharedMutex : IDisposable
    {
        readonly Mutex mutex;

        public SharedMutex(string name)
        {
            bool m = Mutex.TryOpenExisting(name, out mutex);

            if (m)
            {
                mutex.WaitOne();
            }
            else
            {
                Thread.Sleep(10); // Simulate a short delay.
                mutex = new Mutex(true, name);
            }
        }

        public void Dispose()
        {
            mutex.ReleaseMutex();
            mutex.Dispose();
        }
    }
}

请注意短暂的延迟Thread.Sleep(10),足以在运行调试版本的系统上引发异常。可能必须增加延迟才能引起其他系统上的异常。

如果这确实是问题所在,这是解决方法:

class SharedMutex : IDisposable
{
    readonly Mutex mutex;

    public SharedMutex(string name)
    {
        mutex = new Mutex(false, name);
        mutex.WaitOne();
    }

    public void Dispose()
    {
        mutex.ReleaseMutex();
        mutex.Dispose();
    }
}

您实际上并不关心它是否是新创建的。 Mutex构造函数会为您解决这个问题。

即使这不是问题,我仍然认为您应该使用上面的实现,因为它没有潜在的竞争条件。

the documentation for the constructor Mutex(bool, string)所述:

如果name不为null且initialOwned为true,则调用线程 仅当结果创建了指定的系统互斥锁时,才拥有该互斥锁 这个电话。由于没有机制来确定 创建了命名系统互斥锁,最好将false指定为 调用此构造方法重载时最初拥有。