繁体   English   中英

System.Timers.Timer.SynchronizingObject ManagedThreadId不是来自主线程

[英]System.Timers.Timer.SynchronizingObject ManagedThreadId not from Main Thread

我正在使用Entity Framework开发WPF应用(MVVM)。 我想以给定的间隔查询SQL Server上的表,以向用户显示更新的DB值。 为此,我在ViewModel中设置了一个计时器,如下所示:

RefreshTimer = new System.Timers.Timer(2000.0);
RefreshTimer.Enabled = true;
RefreshTimer.SynchronizingObject = new Synchronizer();
RefreshTimer.Elapsed += RefreshTimer_Elapsed;

其中,同步器是ISynchronizeInvoke的实现:

internal class Synchronizer : ISynchronizeInvoke
{
    private object _sync = new object();

    public bool InvokeRequired { get { return true; } }

    public IAsyncResult BeginInvoke(Delegate method, object[] args)
    {
        var result = new SimpleAsyncResult();

        ThreadPool.QueueUserWorkItem(delegate {
            result.AsyncWaitHandle = new ManualResetEvent(false);
            try
            {
                result.AsyncState = Invoke(method, args);
            }
            catch (Exception exception)
            {
                result.Exception = exception;
            }
            result.IsCompleted = true;
        });

        return result;
    }

    public object EndInvoke(IAsyncResult result)
    {
        if (!result.IsCompleted)
        {
            result.AsyncWaitHandle.WaitOne();
        }

        return result.AsyncState;
    }

    public object Invoke(Delegate method, object[] args)
    {
        lock (_sync)
        {
            return method.DynamicInvoke(args);
        }
    }
}

internal class SimpleAsyncResult : IAsyncResult
{
    object _state;

    public bool IsCompleted { get; set; }

    public WaitHandle AsyncWaitHandle { get; internal set; }

    public object AsyncState
    {
        get
        {
            if (Exception != null)
            {
                throw Exception;
            }
            return _state;
        }
        internal set
        {
            _state = value;
        }
    }

    public bool CompletedSynchronously { get { return IsCompleted; } }

    internal Exception Exception { get; set; }
}

如果我理解正确,SynchronizingObject应该在创建SynchronizingObject的线程上封送Elapsed事件。 就我而言,这是主线程。 现在考虑以下代码来处理事件:

private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    System.Diagnostics.Debug.WriteLine($"RefreshTimer ManagedThreadID: {Thread.CurrentThread.ManagedThreadId}");
    RefreshTimer.SynchronizingObject.Invoke((Action)(() => { TestMethod(); }), null);
}

public void TestMethod()
{
    System.Diagnostics.Debug.WriteLine($"TestMethod ManagedThreadID: {Thread.CurrentThread.ManagedThreadId}");
}

我将在TestMethod中更新/重新加载Entity对象,以便View将自动显示最新数据。 当我尝试执行此操作时,我收到InvalidOperationException声明,说明调用线程无法访问此对象,因为其他线程拥有该对象

输出为:

RefreshTimer ManagedThreadID: 5
TestMethod ManagedThreadID: 5
RefreshTimer ManagedThreadID: 4
TestMethod ManagedThreadID: 4
RefreshTimer ManagedThreadID: 8
TestMethod ManagedThreadID: 8

我怀疑这就是我得到上述例外的原因。 我希望输出为:

RefreshTimer ManagedThreadID: 5
TestMethod ManagedThreadID: 1
RefreshTimer ManagedThreadID: 4
TestMethod ManagedThreadID: 1
RefreshTimer ManagedThreadID: 8
TestMethod ManagedThreadID: 1

谁能告诉我我在做什么错以及如何使SynchronizingObject.Invoke方法在主线程上执行?

您可以使用调度程序来安排要在调度程序线程上执行的委托:

private void RefreshTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    System.Diagnostics.Debug.WriteLine($"RefreshTimer ManagedThreadID: {Thread.CurrentThread.ManagedThreadId}");
    Application.Current.Dispatcher.Invoke(new Action(() => { TestMethod(); }))
}

您可能还需要阅读以下内容: https : //www.thomasclaudiushuber.com/2017/02/15/how-to-test-code-that-uses-singletons-like-application-current-dispatcher/

暂无
暂无

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

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