从不同步的代码块中调用了对象同步方法?

这个异常提示,读起来晦涩难懂,感觉有必要解释下这句话是什么意思。

这里所说的不同步的代码块,指的是在不同的线程上运行的代码块。

对象同步方法知道是,对象的方法是同步的,应该是在同一个线程上下文中执行。

所以,总的来讲,就是说:本应该在同一个线程调用的方法,现在是在不同的线程上调用,从而引发此异常。

解决思路

针对这类问题的解决思路,就是把不同的代码块,放到同步代码块中,也就是同一个线程中调度。

具体实现方式可以有多种:

  1. 使用固定一个线程调用
  2. 使用同步上下文进行线程同步

第一种方案相对来讲,实现起来比较简单。我们可以直接开一个线程,将调用代码块,post该线程上执行即可。

Mutex为例:

    public class ProcessMutex
    {
        public ProcessMutex(string key)
        {
            Key = key;
        }

        private Mutex mutex;
        public string Key { get; }

        public async Task WaitOneAsync()
        {
            await SingleThread.RunAndWaitAsync(() =>
            {
                if (!Mutex.TryOpenExisting(Key, out mutex))
                {
                    mutex = new Mutex(false, Key);
                }
                mutex.WaitOne();
            });
        }

        public async Task Dispose()
        {
            await SingleThread.RunAndWaitAsync(() =>
            {
                if (mutex != null)
                {
                    mutex.ReleaseMutex();
                    mutex.Dispose();
                }
            });
        }
    }

    //SingleThread.RunAndWaitAsync 就是额外开启的一个常驻线程。当然,这里也可以用UI线程。

第二种方法通过使用SynchronizationContext来实现上下文同步

WPF里面,上下文同步的使用方法和winform稍微有点区别。
WPF本身有实现System.Windows.Threading.DispatcherSynchronizationContext,该上下文可以通过SynchronizationContext.Current来获得。

但是,该上下文只能在UI线程上获取,因为UI线程是个常驻线程。如果是在多线程中,SynchronizationContext.Current将为null

这个时候需要通过手动设置的方式进行注入:

//为当前上下文进行赋值操作
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

//需要让当前线程等待(不让线程退出)
//……省略

线程同步的更详细用法,可以参考以下博客: C# dotnet 自己实现一个线程同步上下文

欢迎转载分享,如若转载,请标注署名。

博客链接1:https://huchengv5.gitee.io/

博客链接2:https://huchengv5.github.io/

微信公众号:

承哥技术交流小作坊


本文会经常更新,请阅读原文: https://huchengv5.gitee.io//post/WPF-Mutex%E9%87%8A%E6%94%BE%E9%94%81-%E6%8F%90%E7%A4%BA-%E4%BB%8E%E4%B8%8D%E5%90%8C%E6%AD%A5%E7%9A%84%E4%BB%A3%E7%A0%81%E5%9D%97%E4%B8%AD%E8%B0%83%E7%94%A8%E4%BA%86%E5%AF%B9%E8%B1%A1%E5%90%8C%E6%AD%A5%E6%96%B9%E6%B3%95.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名胡承(包含链接: https://huchengv5.gitee.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系