简体   繁体   English

为什么我的代码在同步时不能正常工作?

[英]Why my code doesn't work correctly in synchronization?

I'm using this class to prevent two application (one windows application and one web application running on the same OS) using a shared file at the same time.我正在使用这个 class 来防止两个应用程序(一个 windows 应用程序和一个 web 应用程序在同一操作系统上运行)同时使用共享文件。 But I get the error "Object synchronization method was called from an unsynchronized block of code."但是我收到错误消息“对象同步方法是从未同步的代码块中调用的。”

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

And this is the way I'm using this class这就是我使用这个 class 的方式

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

This class exists in both projects.这个 class 存在于两个项目中。

Note: I am not looking for a solution to the problem to access the files, I need to know why my code has problem.注意:我不是在寻找访问文件问题的解决方案,我需要知道为什么我的代码有问题。 Because I want to use this code for other purposes also.因为我也想将此代码用于其他目的。 Thank you.谢谢你。

I think that this is likely caused by a race condition (as suggested by /u/Sinatr in the comments to the question).我认为这可能是由竞争条件引起的(正如 /u/Sinatr 在对问题的评论中所建议的那样)。

The following code reproduces the issue:以下代码重现了该问题:

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

Note the short delay Thread.Sleep(10) which is enough to provoke the exception on my system, running the debug build.请注意Thread.Sleep(10)的短暂延迟,这足以在我的系统上引发异常,运行调试版本。 The delay may have to be increased to provoke the exception on other systems.可能必须增加延迟才能在其他系统上引发异常。

If this is indeed the problem, this is how to fix it:如果这确实是问题所在,那么解决方法如下:

class SharedMutex : IDisposable
{
    readonly Mutex mutex;

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

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

You don't really care if it was newly created.你真的不在乎它是否是新创建的。 The Mutex constructor takes care of that for you. Mutex 构造函数会为您处理这些事情。

Even if this isn't the problem, I still think you should use the implementation above, since it does not have a potential race condition.即使这不是问题,我仍然认为您应该使用上面的实现,因为它没有潜在的竞争条件。

As the documentation for the constructor Mutex(bool, string) states:正如构造函数Mutex(bool, string)的文档所述

If name is not null and initiallyOwned is true, the calling thread owns the mutex only if the named system mutex was created as a result of this call.如果 name 不是 null 且 initiallyOwned 为 true,则调用线程仅在指定的系统互斥锁作为此调用的结果创建时才拥有互斥锁。 Since there is no mechanism for determining whether the named system mutex was created, it is better to specify false for initiallyOwned when calling this constructor overload.由于没有确定命名系统互斥量是否已创建的机制,因此最好在调用此构造函数重载时为 initiallyOwned 指定 false。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM