用户启动了多个线程。只使用最后一个线程的结果。

3
我有一个应用程序,用户提供要由线程处理的数据,然后线程将数据返回到UI中。 用户可以通过启动另一个线程来更改输入。如何正确忽略旧的返回线程?
我注意到通常有两种解决方案。 1)在调用中检查当前输入数据是否与线程使用的相同 2)等待第一个线程结束后再启动另一个线程
第一种解决方案可行,但如果用户再次更改输入为相同的值,则仍可能使用较旧的线程,并且我会得到两个或更多刷新UI的相同数据 第二个选项响应速度较慢,特别是当线程中的计算运行时间较长时,因为我们必须等待每个线程完成其工作。
我创建了下面的解决方案,我想知道它是否是处理它的适当方式,或者我需要添加其他检查或锁定。
private Thread lastThread;

private void ButtonClick(object sender, MouseButtonEventArgs e)
    {
    this.lastThread= new Thread(delegate () { Update(someData); });
    this.lastThread.Start();
    }

private void Update(int someData) {

    Thread currThread = Thread.CurrentThread;

    Application.Current.Dispatcher.Invoke(new Action(() => EndUpdate(
        someDataCalculated,
        currThread
        )));

private void EndUpdate(int someDataCalculated, Thread senderThread) {

        if (senderThread == this.lastThread)
            {
            // right thread, do some work in UI
           }

  }

如果可能的话,我建议使用带有 CancellationToken 的 Task。请注意,取消是协作式的。 - ProgrammingLlama
如果这个任务计算量如此之大,你确定要让用户有能力创建他想要的数量吗?你知道有些用户会滥用你的按钮。 - John Wu
这类似于自动完成功能。用户可以输入许多字母,您不会在提供响应之前阻止输入下一个字母。相反,会应用一些延迟来防止创建太多的线程。 - Krzysztof Dk
我无法使用任务(Task),因为在我的情况下线程必须是STA。我还有点担心取消令牌(Cancellation Token)可能不起作用,因为它需要调用主线程。所以,我可以检查它的状态的最后一个地方是在调用之前,而调用可能会等待UI线程,在那里实际上用户可能启动新线程。 - Krzysztof Dk
如何正确忽略旧的返回线程?对每个请求传递一个int值参数。为每个请求递增它。跟踪最近响应的int值。忽略任何int值在最近响应之前的响应。您可能需要使用锁来处理同时到达的两个响应。 - mjwills
2个回答

3
如果用户调用了几个线程,并且可以在任何一个线程中更新数据,我认为应该添加一个LastUpdated属性,并跟踪用户进行更改的时间,然后通过比较线程来确定哪个是最新的。

1

你应该使用一种专门设计来处理这种情况的工具,那就是微软的响应式框架。

你只需要编写以下代码:

    public MainWindow()
    {
        InitializeComponent();

        IDisposable subscription =
            Observable
                .FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
                    h => Button.MouseLeftButtonDown += h,
                    h => Button.MouseLeftButtonDown -= h)
                .Select(x => Observable.Start(() => { Update(42); return 42; }))
                .Switch()
                .ObserveOnDispatcher()
                .Subscribe(x =>
                {
                    // latest result only, do some work in UI
                });
    }

这段代码通过在后台线程上调用Update(42); return 42;来响应MouseLeftButtonDown事件。然后调用.Switch(),它是一个特殊的运算符,确保只返回最新调用Observable.Start(() => { Update(42); return 42; })的结果(无论先前的结果是否先出现)。然后使用.ObserveOnDispatcher()操作将其传回UI线程。最后,.Subscribe(x =>允许UI在线程上响应返回的值。

请记住,问题中的示例代码并不完整,因此我进行了一些修改,但我认为您已经理解了。

subscription上调用.Dispose()会分离事件处理程序。

只需NuGet“System.Reactive.Windows.Threading”以获取位,并在代码中添加using System.Reactive.Linq;即可获得所需的扩展方法。

如果需要进一步解释,请告诉我。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接