如何正确地等待来电Dispatcher.Invoke在WPF应用程序完成多线程 [英] How to properly wait for multiple threads that call Dispatcher.Invoke to finish in WPF application

查看:239
本文介绍了如何正确地等待来电Dispatcher.Invoke在WPF应用程序完成多线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WPF应用程序揭开序幕3线程,需要等待他们完成。我看了这里的许多职位与此处理,但没有似乎解决那里的线程代码调用Dispatcher.Invoke或Dispatcher.BeginInvoke的情况。如果我使用线程的join()方法或ManualResetEvent的,上调用调用的线程块。下面是一个丑陋的解决方案的,似乎工作的简化代码片段:

I have a WPF application that kicks off 3 threads and needs to wait for them to finish. I have read many posts here that deal with this but none seem to address the situation where the thread code calls Dispatcher.Invoke or Dispatcher.BeginInvoke. If I use the thread's Join() method or a ManualResetEvent, the thread blocks on the Invoke call. Here's a simplified code snippet of an ugly solution that seems to work:

class PointCloud
{
    private Point3DCollection points = new Point3DCollection(1000);
    private volatile bool[] tDone = { false, false, false };
    private static readonly object _locker = new object();

    public ModelVisual3D BuildPointCloud()
    {
        ...
        Thread t1 = new Thread(() => AddPoints(0, 0, 192));
        Thread t2 = new Thread(() => AddPoints(1, 193, 384));
        Thread t3 = new Thread(() => AddPoints(2, 385, 576));
        t1.Start();
        t2.Start();
        t3.Start();

        while (!tDone[0] || !tDone[1] || !tDone[2]) 
        {
            Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
            Thread.Sleep(1);
        }

        ...
    }

    private void AddPoints(int scanNum, int x, int y)
    {
        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                z = FindZ(x, y);

                if (z == GOOD_VALUE)
                {
                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal,
                      (ThreadStart)delegate()
                      {
                          Point3D newPoint = new Point3D(x, y, z);
                          lock (_locker)
                          {
                              points.Add(newPoint);
                          }
                      }
                  );
                } 
            }
        }
        tDone[scanNum] = true;
    }
}

from the main WPF thread...
PointCloud pc = new PointCloud();
ModelVisual3D = pc.BuildPointCloud();
...



有关如何改善这种代码任何想法,将不胜感激。看起来这应该是一个非常普遍的问题,但我似乎无法找到它妥善解决的任何地方。

Any ideas about how to improve this code would be much appreciated. It seems like this should be a very common problem, but I can't seem to find it properly addressed anywhere.

推荐答案

假设你可以使用.NET 4中,我将告诉你如何做到这一点,避免在线程共享可变状态更清洁的方式(因此,避免锁定)。

Assuming you can use .NET 4, I'm going to show you how to do this in a much cleaner way that avoids sharing mutable state across threads (and thus, avoids locking).

class PointCloud
{
    public Point3DCollection Points { get; private set; }

    public event EventHandler AllThreadsCompleted;

    public PointCloud()
    {
        this.Points = new Point3DCollection(1000);

        var task1 = Task.Factory.StartNew(() => AddPoints(0, 0, 192));
        var task2 = Task.Factory.StartNew(() => AddPoints(1, 193, 384));
        var task3 = Task.Factory.StartNew(() => AddPoints(2, 385, 576));
        Task.Factory.ContinueWhenAll(
            new[] { task1, task2, task3 }, 
            OnAllTasksCompleted, // Call this method when all tasks finish.
            CancellationToken.None, 
            TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext()); // Finish on UI thread.
    }

    private void OnAllTasksCompleted(Task<List<Point3D>>[] completedTasks)
    {
        // Now that we've got our points, add them to our collection.
        foreach (var task in completedTasks)
        {
            task.Result.ForEach(point => this.points.Add(point));
        }

        // Raise the AllThreadsCompleted event.
        if (AllThreadsCompleted != null)
        {
            AllThreadsCompleted(this, EventArgs.Empty);
        }
    }

    private List<Point3D> AddPoints(int scanNum, int x, int y)
    {
       const int goodValue = 42;
       var result = new List<Point3D>(500);
       var points = from pointX in Enumerable.Range(0, x)
                    from pointY in Enumerable.Range(0, y)
                    let pointZ = FindZ(pointX, pointY)
                    where pointZ == goodValue
                    select new Point3D(pointX, pointX, pointZ);
       result.AddRange(points);
       return result;
    }
}



这个类的消费很简单:

Consumption of this class is easy:

// On main WPF UI thread:
var cloud = new PointCloud();
cloud.AllThreadsCompleted += (sender, e) => MessageBox.Show("all threads done! There are " + cloud.Points.Count.ToString() + " points!");



此技术的说明



想想线程是不同的:而不是试图对共享数据(例如,您点列表)线程同步访问,而不是做繁重的后台线程,但不发生变异的共享状态(例如,不要把点列表中添加任何东西)。对我们来说,这意味着遍历X和Y,并找到Z,而不是将它们添加到积分榜的后台线程。一旦我们创建的数据,让UI线程知道我们就大功告成了,让他采取加分的名单照顾。

Explanation of this technique

Think about threading differently: instead of trying to synchronize thread access to shared data (e.g. your point list), instead do heavy lifting on the background thread but don't mutate any shared state (e.g. don't add anything to the points list). For us, this means looping over X and Y and finding Z, but not adding them to the points list in the background thread. Once we've created the data, let the UI thread know we're done and let him take care of adding the points to the list.

该技术的优点不共享任何可变状态 - 只有1线程访问点集合。它也有不需要任何锁或显式同步的优势

This technique has the advantage of not sharing any mutable state -- only 1 thread accesses the points collection. It also has the advantage of not requiring any locks or explicit synchronization.

它有另外一个重要的特点:你的UI线程不会阻止。这通常是个好东西,你不希望你的应用程序出现冰冻。如果阻塞UI线程是一个要求,我们不得不返工这种解决方案一点。

It has another important characteristic: your UI thread won't block. This is generally a good thing, you don't want your app to appear frozen. If blocking the UI thread is a requirement, we'd have to rework this solution a bit.

这篇关于如何正确地等待来电Dispatcher.Invoke在WPF应用程序完成多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆