【问题标题】:WPF multitasking along with a timer and background workerWPF 多任务处理以及计时器和后台工作人员
【发布时间】:2013-08-30 09:44:40
【问题描述】:

几天前,我开始在我的 WPF 应用程序中使用任务来实现并行化。我的应用程序需要每 5 秒执行一次工作。这项工作必须由 4 个任务并行化。除此之外,还需要实现后台工作程序,以避免在按任务执行工作时冻结 UI。我找到了很多例子来理解任务是如何工作的。但是,我找不到任何简单的示例来理解任务如何与计时器、后台工作人员和锁一起工作。我根据自己的理解写了一个简单的例子。请给我一些关于我是否正确执行的建议。这样,我将对 WPF 中的多任务处理有更好的理解。我期待着您的回复。

       namespace timer_and_thread
            {
                /// <summary>
                /// Interaction logic for MainWindow.xaml
                /// </summary>
                public partial class MainWindow : Window
                {
                    DispatcherTimer TimerObject;
                    Task[] tasks;
                    readonly object _countLock = new object();
                    int[] Summ = new int[10];
                    int Index = 0;
                    int ThreadsCounter = 0;


                    public MainWindow()
                    {
                        InitializeComponent();
                        TimerObject = new DispatcherTimer();
                        TimerObject.Tick += new EventHandler(timer_Elapsed);
                        TimerObject.Interval = new TimeSpan(0, 0, 5);
                    }


                    // call the method every 5 seconds
                    private void timer_Elapsed(object sender, EventArgs e)
                    {
                        TimerObject.Stop();

                        // 4 - is the tasks' count
                        ThreadsCounter = 4;
                        Index = 0;

                        tasks = new Task[4];
                        tasks[0] = Task.Factory.StartNew(() => DoSomeLongWork());
                        tasks[1] = Task.Factory.StartNew(() => DoSomeLongWork());
                        tasks[2] = Task.Factory.StartNew(() => DoSomeLongWork());
                        tasks[3] = Task.Factory.StartNew(() => DoSomeLongWork());

                         // wait untill all tasks complete
                         while (ThreadsCounter != 0) ;


                         TimerObject.Start();

                    }


                    private void DoSomeLongWork()
                    {
                        while (Index < Summ.Length)
                        {


                            // lock the global variable from accessing by multiple threads at a time
                        int localIndex = Interlocked.Increment(ref Index) - 1;

                            //I wrote rundom number generation just a an example of doing some calculation and getting some result. It can also be some long calculation. 
                            Random rnd = new Random();
                            int someResult = rnd.Next(1, 100000);

                            // lock the global variable (Summ) to give it the result of calculation 
                            lock (_countLock)
                            {
                                Summ[localIndex] = someResult;
                            }      
                        }


                        Interlocked.Decrement(ref ThreadsCounter);
                        return;

                    }

                    // button by which I start the application working
                    private void Start_Button_Click_1(object sender, RoutedEventArgs e)
                    {
                        TimerObject.Start();
                    }

                }
            }

我还有 2 个问题:

  1. 我可以使用任务代替后台工作人员吗?

  2. 据我所知,使用锁定是为了防止线程访问全局变量。但是,要通过线程访问 UI 元素,我应该使用 Dispatcher。对吧?

Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {

label1.Content = "Some text";
}));

【问题讨论】:

  • 1) 是的尝试 Task.Factory.StartNew(() => StartThreads(null, null));而不是后台工作人员自己查看 2) 正确 :)
  • 感谢您的回答。代码呢,把task、timer和BackgroundWorker一起实现是不是正确的方式?
  • 重点是,后台工作人员什么都不做。您不需要它来启动任务,它们无论如何都在自己的线程中运行
  • 感谢您的回复。现在我编辑了代码。

标签: wpf multithreading task-parallel-library multitasking


【解决方案1】:

在我看来,您甚至不需要BackgroundWorker。目前您有:

private void timer_Elapsed(object sender, EventArgs e)
{
    TimerObject.Stop();

    BackgroundWorker backgroundWorkerObject = new BackgroundWorker();
    backgroundWorkerObject.DoWork += new DoWorkEventHandler(StartThreads);
    backgroundWorkerObject.RunWorkerAsync();

    TimerObject.Start();
}


private void StartThreads(object sender, DoWorkEventArgs e)
{
    tasks = new Task[4];
    tasks[0] = Task.Factory.StartNew(() => DoSomeLongWork());
    tasks[1] = Task.Factory.StartNew(() => DoSomeLongWork());
    tasks[2] = Task.Factory.StartNew(() => DoSomeLongWork());
    tasks[3] = Task.Factory.StartNew(() => DoSomeLongWork());

    // Give the tasks a second to start.
    Thread.Sleep(1000);
}

BackgroundWorker 所做的只是启动线程,然后由于某种未知原因等待一秒钟再继续。我看不出有什么特别的原因让您在让BackgroundWorker 退出之前等待一秒钟。

如果您消除了不必要的延迟,那么您可以将代码更改为:

private void timer_Elapsed(object sender, EventArgs e)
{
    TimerObject.Stop();

    tasks = new Task[4];
    tasks[0] = Task.Factory.StartNew(() => DoSomeLongWork());
    tasks[1] = Task.Factory.StartNew(() => DoSomeLongWork());
    tasks[2] = Task.Factory.StartNew(() => DoSomeLongWork());
    tasks[3] = Task.Factory.StartNew(() => DoSomeLongWork());

    TimerObject.Start();
}

您遇到的另一个问题是,如果在计时器已过事件再次发生之前这些任务未完成,您将覆盖 Tasks 数组并且您的计算(特别是 Sum 数组)将包含来自两组计算的信息。如果这些线程需要超过 5 秒才能完成,那么您在此处使用全局变量将会给您带来麻烦。

我将假设您的 DoSomeLongWork 方法只是一个占位符,并且不会对此进行评论。不过,我会提到,没有必要为了增加共享变量而锁定。而不是:

int localIndex;
// lock the global variable from accessing by multiple threads at a time
lock (_countLock)
{
    localIndex = Index;
    Index++;
}

你可以写:

localIndex = Interlocked.Increment(ref Index, 1) - 1;

Interlocked.Increment 以原子方式递增值并返回新值。

【讨论】:

  • 谢谢。我做了你推荐给我的改变。我还改变了启动计时器的方式。它仅在所有任务完成其工作时启动一次。请看看我是否做得正确。现在我有一个 while 循环 - while (ThreadsCounter != 0) 冻结了我的 UI。我现在应该怎么做才能防止 UI 冻结?
【解决方案2】:

要回答 8 月 30 日的问题,您可以查看 Tasks 的 ContinueWhenAll 函数,而不是 while 循环,并将计时器的开始时间移至该函数。它需要一个任务列表,然后当所有任务都完成后,创建另一个任务并运行它。这应该可以防止您的 UI 冻结,但如果它必须每 5 秒运行一次,而不是在完成之前的任务后每 5 秒运行一次,您可能需要研究一些不同的东西。

http://msdn.microsoft.com/en-us/library/dd321473%28v=vs.110%29.aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多