通过后台工作人员进行枚举

时间:2016-01-06 03:28:10

标签: c# .net backgroundworker enumeration

有没有办法列举所有后台工作人员?目前,我已经编写了一个小方法,我在创建新工作时添加了这个方法。一次只能运行一个工作人员,因此检查方法是:

    private bool CheckForWorkers()  // returns true if any background workers are currently running
    {
        bool ret = false;

        if (bgWorkerFoo.IsBusy || bgWorkerMeh.IsBusy || bgWorkerHmpf.IsBusy || bgWorkerWorkyWorky.IsBusy || bgWorkerOMGStahp.IsBusy)
        {
            ret = true;
        }

        return ret;
    }

单击按钮启动工作人员时,click方法执行此操作:

        if (CheckForWorkers())
        {
            MessageBox.Show("File generation already in progress.  Please wait.", "Message");
            return;
        }

        else
        {
            bgWorkerFoo.RunWorkerAsync();
        }

我想清理我的CheckForWorkers()方法,以便在为不同的任务创建新工作人员的任何时候我都不需要添加它,但我似乎无法找到任何方式来运行与应用程序关联的每个工作程序。也许没有办法?在使用之前是否存在(实例化)所有工人?

3 个答案:

答案 0 :(得分:1)

为什么不做类似的事情?

Worker[] Workers => new[] { bgWorkerFoo, bgWorkerMeh, bgWorkerHmpf, bgWorkerWorkyWorky, bgWorkerOMGStahp };
private bool CheckForWorkers() 
{
    return Workers.Any(w => w != null && w.IsBusy);
}

您可能也需要在将来引用工作人员的集合,因此无论如何将它们放入集合中是有意义的

或者,对于非C#6语法,有点丑陋:

private Worker[] Workers { get { return new[] { bgWorkerFoo, bgWorkerMeh, bgWorkerHmpf, bgWorkerWorkyWorky, bgWorkerOMGStahp }; } }
private bool CheckForWorkers() 
{
    return Workers.Any(w => w != null && w.IsBusy);
}

要动态获取班级中的所有字段/属性,您可以执行以下操作:

private IEnumerable<Worker> GetWorkers()
{
    var flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
    var fields = GetType().GetFields(flags);
    var fieldValues = fields.Select(f => f.GetValue(this)).OfType<Worker>();

    var properties = GetType().GetProperties(flags);
    var propertyValues = properties.Select(f => f.GetValue(this)).OfType<Worker>();

    return fieldValues.Concat(propertyValues).Where(w => w != null);
}

private bool CheckForWorkers() 
{
    return GetWorkers().Any(w => w.IsBusy);
}

最好缓存GetWorkers(),但这取决于你的用例。

答案 1 :(得分:1)

我将建议一种可能具有吸引力的替代方法。 Microsoft Reactive Framework为处理并发和线程引入了许多非常有用的功能。主要是框架用于处理IObservable<T>方面的事件源,但框架还提供了许多调度程序来处理不同线程上的处理。

其中一个调度程序称为EventLoopScheduler,这允许您创建在后台线程上运行的调度程序,并且只允许一次执行一个操作。任何线程都可以安排任务和任务,可以立即或将来安排,甚至可以重复安排。

这里的关键点是,您不需要跟踪多个后台工作人员,因为您安排的操作次数并不重要,只会 串联运行

使用Windows窗体时,您可以使用名为ControlScheduler的调度程序,该调度程序允许您设置将操作发布到UI线程的调度程序。

一旦你完成了这两个设置,它们就变得非常容易使用了。

试试这段代码:

var form1 = new Form();
var label1 = new Label();
label1.AutoSize = true;
form1.Controls.Add(label1);
form1.Show();

var background = new EventLoopScheduler();
var ui = new ControlScheduler(form1);

var someValue = -1;
background.Schedule(() =>
{
    var z = 42 * someValue;
    var bgTid = System.Threading.Thread.CurrentThread.ManagedThreadId;
    ui.Schedule(() =>
    {
        var uiTid = System.Threading.Thread.CurrentThread.ManagedThreadId;
        label1.Text = $"{z} calc on {bgTid} updated on {uiTid}";
    });
});

当我运行此代码时,我会在屏幕上显示此表单:

form

显然计算是正确的,可以看出线程ID是不同的。

你甚至可以做更强大的事情:

var booking =
    background.SchedulePeriodic(0, TimeSpan.FromSeconds(1.0), state =>
    {
        var copy = state;
        if (copy % 2 == 0)
        {
            ui.Schedule(() => label1.Text = copy.ToString());
        }
        else
        {
            background.Schedule(() => ui.Schedule(() => label1.Text = "odd"));
        }
        return ++state;
    });

form1.FormClosing += (s, e) => booking.Dispose();

此代码创建一个计时器,以便在后台线程上每秒运行一次。它使用state变量来跟踪它运行的次数,并在偶数时使用值state更新UI,否则,在其自己的调度程序上调度一些代码,在UI上安排使用值label1.Text更新"odd"。它可以变得非常复杂,但所有内容都是为您序列化和同步的。由于这创建了一个计时器,因此有一种机制可以关闭计时器并调用booking.Dispose()

当然,由于这是使用Reactive Framework,您可以使用标准的observable来执行上述操作,如下所示:

var query =
    from n in Observable.Interval(TimeSpan.FromSeconds(1.0), background)
    select n % 2 == 0 ? n.ToString() : "odd";

var booking = query.ObserveOn(ui).Subscribe(x => label1.Text = x);

请注意,使用了相同的调度程序。

答案 2 :(得分:0)

如果你需要为(可能很大或无限制)数量的变量执行相同的任务,这可能意味着它们在某种程度上是“同质的”,因此应该可以合并到某种“注册表”容器中代替。

E.g。我以前做过这样的事情:

var thread = new BgWorker();
pool.Add(thread);
<...>
foreach (var thread in pool) {
    do_something();
}

注册表的具体实现可以是任何东西,具体取决于您需要对对象执行的操作(例如,如果您需要在其他范围内获取特定的字典而不是创建它的字典)。