0

可能重复:
调用线程无法访问此对象,因为不同的线程拥有它

我在我的 Serialport 程序中收到此错误:

调用线程无法访问此对象,因为不同的线程拥有它。wpf

我有一个定时器功能来更新我的用户界面,同时用户可以按下任何按钮与串行端口通信。

public MainWindow()
{
    InitializeComponent();

    OpenPort();

    Timer aTimer = new System.Timers.Timer();
    aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    aTimer.Interval = 5000;
    aTimer.Enabled = true;
}

这是我的两个功能:

private void OnTimedEvent(object sender, EventArgs e)
{
    CheckStatus(); // this function will update my UI base of information will receive from serial port         
}

private void Processing()
{
    // Do stuff with serial port
}
4

3 回答 3

2

我有一个定时器功能来更新我的 UI

使用Dispatcher.Invoke从其他线程修改 GUI。

在 WPF 中,只有创建 DispatcherObject 的线程才能访问该对象。例如,从主 UI 线程分离的后台线程无法更新在 UI 线程上创建的 Button 的内容。为了让后台线程访问 Button 的 Content 属性,后台线程必须将工作委托给与 UI 线程关联的 Dispatcher。这是通过使用 Invoke 或 BeginInvoke 来完成的。Invoke 是同步的,而 BeginInvoke 是异步的。

于 2013-02-04T04:40:27.603 回答
2

正如您的错误所说,问题是您启动了一个具有特定功能的线程。在您的情况下,当您使用 启动线程时OnTimedEvent,此函数在与主应用程序不同的范围内运行:

在此处输入图像描述

这意味着:无论您尝试从OnTimedEvent与您的主程序相关的什么访问,反之亦然,都会失败,因为它不在同一范围内运行。你能做什么?我使用代表团。

首先,您需要知道Context您的主应用程序在哪个平台上运行。因此,为您的主应用程序创建一个全局变量:

private SynchronizationContext uiContext;

然后,当您启动应用程序时,可以像这样捕获上下文的状态:

public MainWindow()
{
    uiContext = SynchronizationContext.Current;
}

有了这个,您可以了解您的应用程序或不同功能的真实上下文。因此,如果您想知道您OnTimedEvent是否在不同的上下文中运行并委托它,您将执行以下操作:

if (SynchronizationContext.Current != uiContext)
{
    uiContext.Post(delegate { EnableMenuAndButtons(o, args); }, null);
}
else
{
    // Do your normal stuff here
}

有了这个,你应该能够在你的线程和 vivecersa 中使用变量。

oargs是我传递的变量。我正在使用 WPF,但您可以将此示例与常规 Windows 窗体一起使用(但您必须使用它的特定方法)。您需要的库是System.Threading.

澄清:对你来说,像这样:

private void OnTimedEvent(object sender, EventArgs e)
{
    if (SynchronizationContext.Current != uiContext)
    {
        uiContext.Post(delegate { EnableMenuAndButtons(sender, e); }, null);
    }
    else
    {
        // Do your normal stuff here
        CheckStatus(); // this function will update my UI base of information will receive from serial port 
    }
}

}

于 2013-02-04T08:53:32.440 回答
0

请注意,System.Timers.Timertick 事件是在不同于主 UI 线程的另一个线程中生成的。您可以通过以下方式轻松验证

System.Diagnostics.Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);

在这种情况下,您需要调用Dispatcher.Invoke以更新 UI 元素,正如 @Habib 指出的那样。

另一方面,您也可以使用DispatcherTimer(WPF 引入的计时器)而不是System.Timers.Timer. 随着DispatcherTimertick 事件将在主 UI 线程中引发,您可以毫无问题地修改 UI。

于 2013-02-04T07:45:18.123 回答