Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 等待所有排队的后台工作人员完成?_C#_Wpf_Multithreading_.net 4.0 - Fatal编程技术网

C# 等待所有排队的后台工作人员完成?

C# 等待所有排队的后台工作人员完成?,c#,wpf,multithreading,.net-4.0,C#,Wpf,Multithreading,.net 4.0,我正在试用我找到的一些QueuedBackgroundWorker类。它工作得很好,只是我想知道如何才能等到所有排队的工作人员都完成?例如,如果用户要关闭程序,我希望程序等待所有工作人员完成后再关闭 我试着在GUI线程上执行类似的操作,但它似乎阻止了: try { while (myWorkerQueue.Queue.Count > 0) ; } catch (InvalidOperationExce

我正在试用我找到的一些
QueuedBackgroundWorker
类。它工作得很好,只是我想知道如何才能等到所有排队的工作人员都完成?例如,如果用户要关闭程序,我希望程序等待所有工作人员完成后再关闭

我试着在GUI线程上执行类似的操作,但它似乎阻止了:

        try
        {
            while (myWorkerQueue.Queue.Count > 0) ;

        }
        catch (InvalidOperationException)
        {

        }
还尝试了
while(myWorkerQueue.Queue.Peek()!=null)
并得到了相同的结果

QueuedBackgroundWorker的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;

/// <summary>
/// This is thread-safe
/// </summary>
public class QueuedBackgroundWorker
{
    #region Constructors

        public QueuedBackgroundWorker() { }

    #endregion

    #region Properties

    Queue<object> Queue = new Queue<object>();

    object lockingObject1 = new object();

    public delegate void WorkerCompletedDelegate<K>(K result, Exception error);

    #endregion

    #region Methods

    /// <summary>
    /// doWork is a method with one argument
    /// </summary>
    /// <typeparam name="T">is the type of the input parameter</typeparam>
    /// <typeparam name="K">is the type of the output result</typeparam>
    /// <param name="inputArgument"></param>
    /// <param name="doWork"></param>
    /// <param name="workerCompleted"></param>
    public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument, WorkerCompletedDelegate<K> workerCompleted)
    {
        BackgroundWorker bw = GetBackgroundWorker<T,K>(doWork, workerCompleted);

        Queue.Enqueue(new QueueItem(bw, inputArgument));

        lock (lockingObject1)
        {
            if (Queue.Count == 1)
            {
                ((QueueItem)this.Queue.Peek()).RunWorkerAsync();      
            }
        }
    }

    /// <summary>
    /// Use this method if you don't need to handle when the worker is completed
    /// </summary>
    /// <param name="doWork"></param>
    /// <param name="inputArgument"></param>
    public void RunAsync<T,K>(Func<T, K> doWork, T inputArgument)
    {
        RunAsync(doWork, inputArgument, null);
    }

    private BackgroundWorker GetBackgroundWorker<T, K>(Func<T, K> doWork, WorkerCompletedDelegate<K> workerCompleted)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.WorkerReportsProgress = false;
        bw.WorkerSupportsCancellation = false;

        bw.DoWork += (sender, args) =>
        {
            if (doWork != null)
            {
                args.Result = (K)doWork((T)args.Argument);
            }
        };

        bw.RunWorkerCompleted += (sender, args) =>
        {
            if (workerCompleted != null)
            {
                workerCompleted((K)args.Result, args.Error);
            }
            Queue.Dequeue();
            lock (lockingObject1)
            {
                if (Queue.Count > 0)
                {
                    ((QueueItem)this.Queue.Peek()).RunWorkerAsync();                  
                }
            }
        };
        return bw;
    }

    #endregion
}

public class QueueItem
{
    #region Constructors

    public QueueItem(BackgroundWorker backgroundWorker, object argument)
    {
        this.BackgroundWorker = backgroundWorker;
        this.Argument = argument;
    }

    #endregion

    #region Properties

    public object Argument { get; private set; }

    public BackgroundWorker BackgroundWorker { get; private set; }

    #endregion

    #region Methods

    public void RunWorkerAsync()
    {
        this.BackgroundWorker.RunWorkerAsync(this.Argument);
    }

    #endregion
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Web;
使用系统组件模型;
/// 
///这是线程安全的
/// 
公共类QueuedBackgroundWorker
{
#区域构造函数
公共队列BackgroundWorker(){}
#端区
#区域属性
队列=新队列();
对象锁定对象1=新对象();
公共委托无效WorkerCompletedElegate(K结果,异常错误);
#端区
#区域方法
/// 
///doWork是一个只有一个参数的方法
/// 
///是输入参数的类型
///是输出结果的类型
/// 
/// 
/// 
public void RunAsync(Func doWork、T inputArgument、WorkerCompletedLegate workerCompleted)
{
BackgroundWorker bw=GetBackgroundWorker(doWork,workerCompleted);
Enqueue(新的QueueItem(bw,inputArgument));
锁定(锁定对象1)
{
如果(Queue.Count==1)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
}
/// 
///如果不需要在工作程序完成时处理,请使用此方法
/// 
/// 
/// 
public void RunAsync(Func doWork,T inputArgument)
{
RunAsync(doWork、inputArgument、null);
}
私人后台工作人员GetBackgroundWorker(Func doWork、WorkerCompletedLegate workerCompleted)
{
BackgroundWorker bw=新的BackgroundWorker();
bw.WorkerReportsProgress=false;
bw.workersupport扫描单元=false;
bw.DoWork+=(发送方,参数)=>
{
如果(doWork!=null)
{
args.Result=(K)doWork((T)args.Argument);
}
};
bw.RunWorkerCompleted+=(发送方,参数)=>
{
如果(workerCompleted!=空)
{
workerCompleted((K)args.Result,args.Error);
}
Queue.Dequeue();
锁定(锁定对象1)
{
如果(Queue.Count>0)
{
((QueueItem)this.Queue.Peek()).RunWorkerAsync();
}
}
};
返回bw;
}
#端区
}
公共类队列项
{
#区域构造函数
公共队列项(BackgroundWorker BackgroundWorker,对象参数)
{
this.BackgroundWorker=BackgroundWorker;
这个参数=参数;
}
#端区
#区域属性
公共对象参数{get;private set;}
公共后台工作程序后台工作程序{get;private set;}
#端区
#区域方法
public void RunWorkerAsync()
{
this.BackgroundWorker.RunWorkerAsync(this.Argument);
}
#端区
}

如果你这样做

while (myWorkerQueue.Queue.Count > 0) ;
你的while循环占用了太多的资源,以至于你的后台线程已经没有了。它似乎被封锁了

如果您想保持while循环(我不建议这样做),至少让您的后台线程可以工作:

while (myWorkerQueue.Queue.Count > 0)
    System.Threading.Thread.Sleep(1000);
正如您在评论中所说的,最简单的解决方案是挂接关闭事件,并在myWorkerQueue.Queue.Count>0时中止它


一个更优雅的解决方案是创建一个带有进度条的模式表单,在表单关闭时显示进度条,如果myWorkerQueue.Queue.Count>0,则进度条将在剩余后台工作人员完成时进行…

如果您这样做

while (myWorkerQueue.Queue.Count > 0) ;
你的while循环占用了太多的资源,以至于你的后台线程已经没有了。它似乎被封锁了

如果您想保持while循环(我不建议这样做),至少让您的后台线程可以工作:

while (myWorkerQueue.Queue.Count > 0)
    System.Threading.Thread.Sleep(1000);
正如您在评论中所说的,最简单的解决方案是挂接关闭事件,并在myWorkerQueue.Queue.Count>0时中止它


一个更优雅的解决方案是创建一个带有进度条的模式表单,在表单关闭时显示进度条,如果myWorkerQueue.Queue.Count>0,则进度条将随着剩余后台工作人员的完成而进行…

您绝对必须使用BackgroundWorker吗。NET 4引入了
Task
API(简称任务并行库或TPL):您可以启动多个任务并使用
Task。Whalll
提供仅在所有任务完成时执行的延续:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var myTasks = new List<Task<BitmapImage>>();
        // Task<T>.Factory.StartNew starts the given method on a Thread Pool thread
        myTasks.Add(Task<BitmapImage>.Factory.StartNew(LoadPicture1));
        myTasks.Add(Task<BitmapImage>.Factory.StartNew(LoadPicture2));

        // The important part: Task.WhenAll waits asynchronously until all tasks
        // in the collection finished sucessfully. Only then, the lambda that is
        // given to the ContinueWith method is executed. The UI thread does not block
        // in this case.
        Task.WhenAll(myTasks)
            .ContinueWith(task =>
                          {
                              foreach (var bitmapImage in task.Result)
                              {
                                  var image = new Image { Source = bitmapImage };
                                  ImageStackPanel.Children.Add(image);
                              }
                          },
                          TaskScheduler.FromCurrentSynchronizationContext());
    }

    private BitmapImage LoadPicture1()
    {
        return LoadImageFile("Picture1.jpg");
    }

    private BitmapImage LoadPicture2()
    {
        // Simulate that this loading process takes a little bit longer
        Thread.Sleep(1000);
        return LoadImageFile("Picture2.jpg");
    }

    private BitmapImage LoadImageFile(string path)
    {
        using (var fileStream = new FileStream(path, FileMode.Open))
        {
            var bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.StreamSource = fileStream;
            bitmapImage.EndInit();
            bitmapImage.Freeze();
            return bitmapImage;
        }
    }
}
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
private void按钮base_OnClick(对象发送方,RoutedEventTarget e)
{
var myTasks=新列表();
//Task.Factory.StartNew在线程池线程上启动给定的方法
添加(Task.Factory.StartNew(LoadPicture1));
添加(Task.Factory.StartNew(LoadPicture2));
//重要部分:Task.WhenAll异步等待所有任务
//在集合中成功完成。只有到那时,lambda
//执行给定给ContinueWith方法。UI线程不阻塞
//在这种情况下。
Task.WhenAll(我的任务)
.ContinueWith(任务=>
{
foreach(task.Result中的变量bitmapImage)
{
var image=new image{Source=bitmapImage};
ImageStackPanel.Children.Add(图像);