Dispatcher.CheckAccess() 无法在我的控制台应用程序中工作,有更好的方法吗

发布于 2024-08-29 21:06:18 字数 968 浏览 2 评论 0原文

我用 WPF / VB 编写了一个应用程序,并将业务逻辑和 UI 分离到不同的项目中。

业务层使用在不同线程上运行的串行端口,现在我尝试为同一业务层编写命令行接口,但在调用 .Invoke() 时似乎会失败。 (没有错误,只是不起作用)

我很确定我必须添加 checkaccess 和 .invoke 的原因是因为我有在处理串行端口数据期间会更改的集合,并且希望 NotifyCollectionChanged 由 WPF 处理数据绑定。 (我不是 100% 确定的原因是因为几个月前我写了那部分,并且在 GUI 上一切都工作得很好,现在添加控制台应用程序让我重新思考其中的一些)

我希望我的业务层能够运行这些进程在它们创建的线程上,我需要它在我的 GUI 版本和命令行版本中工作。

我是否在业务层中滥用了 Dispatcher?有没有更好的方法来处理来自串口的事件,然后返回主线程处理数据?

更新:

Private Delegate Sub NewDataRecieved(ByVal byteBuffer() As Byte)
Private Sub DataReceived(ByVal byteBuffer() As Byte) Handles _serial.DataRecieved
    If _dispatcher.CheckAccess() Then
        ProcessTheData
    Else
        Dim dataReceivedDelegate As NewDataRecieved
        dataReceivedDelegate = New NewDataRecieved(AddressOf DataReceived)
        _dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer)
    End If
End Sub

I wrote an application in WPF / VB and separated the business logic and UI into different projects.

The business layer uses a serial port which runs on a different thread, Now that I'm trying to write a command line interface for the same business layer, it seems to fail when .Invoke() is called. (no error, just doesn't work)

I'm pretty sure the reason I had to add in checkaccess and .invoke was because I have collections that would be changed during processing the serial port data and wanted the NotifyCollectionChanged to be handled by WPF data binding.
(The reason I'm not 100% sure is because it was months ago I wrote that part and it all worked great from the GUI, now adding the console app has made me rethink some of this)

I would like my business layer to run these processes on the thread they were created, I need this to work from both my GUI version and the command line version.

Am I misusing the Dispatcher in my business layer? Is there a better way to handle an event from the serial port, and then return to the main thread to processes the data?

Updated:

Private Delegate Sub NewDataRecieved(ByVal byteBuffer() As Byte)
Private Sub DataReceived(ByVal byteBuffer() As Byte) Handles _serial.DataRecieved
    If _dispatcher.CheckAccess() Then
        ProcessTheData
    Else
        Dim dataReceivedDelegate As NewDataRecieved
        dataReceivedDelegate = New NewDataRecieved(AddressOf DataReceived)
        _dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer)
    End If
End Sub

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

酒绊 2024-09-05 21:06:19

控制台应用程序中的 WPF?在控制台应用程序中,对特定线程中运行的函数没有限制。您可以处理调用者线程上下文中的任何事件,并在必要时提供同步。

WPF in a Console application? In Console applications there are no restrictions on running function in specific thread. You can handle any event in the caller thread context, providing synchronization if necessary.

内心激荡 2024-09-05 21:06:19

另一种选择:在需要 Dispatcher 的线程中,使用 Dispatcher.CurrentDispatcher 静态属性。如果尚未为线程创建调度程序,则会自动创建它(如果可能)。您可以将此值存储在某处以允许其他线程使用调度程序。

在代码中:

public class BusinessLayerThread
{
    public BusinessLayerThread()
    {
        Dispatcher = Dispatcher.CurrentDispatcher;
    }

    public static Dispatcher Dispatcher { get; private set; }
}

An alternative: In the thread where you want a Dispatcher, use the Dispatcher.CurrentDispatcher static property. If a dispatcher has not been created yet for the thread, it is then auto-created (if possible). You could store this value somewhere to allow other threads to use the dispatcher.

In code:

public class BusinessLayerThread
{
    public BusinessLayerThread()
    {
        Dispatcher = Dispatcher.CurrentDispatcher;
    }

    public static Dispatcher Dispatcher { get; private set; }
}
§对你不离不弃 2024-09-05 21:06:18

Invoke 不会执行任何操作,因为您没有运行调度程序,要从调度程序获取任何服务,您必须调用 Dispatcher.Run ( http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.run.aspx )

现在,您的问题是调用 Dispatcher.Run 将使 WPF 控制线程 - 这可能不是您想要在控制台应用程序中执行的操作。

我认为在您的情况下最好的选择是从业务层删除线程同步代码(与调度程序对话的任何内容)并将其放入包装对象中。

WPF 应用程序可以通过使用包装器对象继续像以前一样工作,并且控制台应用程序可以直接使用“原始”业务层。

更新:这是下面的包装器的示例,您必须为原始类的每个公共成员创建一个方法/属性,该成员执行线程工作并调用原始对象。

public class BOWrapper : INotifyPropertyChanged
{
    private BO _bo;
    private Dispather _dispather;

    public BOWrapper(BO bo, Dispatcher dispather)
    {
        _bo = bo;
        _dispather = dispather;
        _bo.PropertyChanged += BOPropertyChanged;
    }

    public string SomeValue
    {
        get { return _bo.SomeValue; }
    }

    private void BOPropertyChanged(object sender, PropertyChangedEventArgs ea)
    {
        _dispatcher.Invoke(
            new Action<PropertyChangedEventArgs>(
                e=>
                {
                    var handler = PropertyChanged;
                    if(handler!=null) handler(this,e);
                }),ea);
    }
}

包装类是 100% 样板代码,您可能可以使用一些代码生成器来创建它,甚至可以使用 DynamicProxy 之类的东西( http://www.castleproject.org/dynamicproxy/index.html ) 在运行时自动生成它。

Invoke doesn't do anything because you don't have a dispatcher running, to get any services from a dispatcher you have to call Dispatcher.Run ( http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.run.aspx )

Now, your problem is that calling Dispatcher.Run will make WPF take control of the thread - and that is probably not what you want to do in a console application.

I think the best option in your situation is to remove the thread synchronization code (anything that talks to a dispatcher) from your business layer and into wrapper objects.

The WPF app can continue to work as before by using the wrapper objects and the console app can use the "raw" business layer directly.

Update: here is an example of the wrapper below, you have to create a method/property for every public member of the original class that does the threading work and calls the original object.

public class BOWrapper : INotifyPropertyChanged
{
    private BO _bo;
    private Dispather _dispather;

    public BOWrapper(BO bo, Dispatcher dispather)
    {
        _bo = bo;
        _dispather = dispather;
        _bo.PropertyChanged += BOPropertyChanged;
    }

    public string SomeValue
    {
        get { return _bo.SomeValue; }
    }

    private void BOPropertyChanged(object sender, PropertyChangedEventArgs ea)
    {
        _dispatcher.Invoke(
            new Action<PropertyChangedEventArgs>(
                e=>
                {
                    var handler = PropertyChanged;
                    if(handler!=null) handler(this,e);
                }),ea);
    }
}

The wrapper class is 100% boilerplate code and you probably can use some code generator to create it, maybe even use something like DynamicProxy ( http://www.castleproject.org/dynamicproxy/index.html ) to generate it automatically at runtime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文