消费者必须等待UI交互的生产者/消费者模式

时间:2016-05-18 17:37:51

标签: c#

过去几天我从这个网站上学到了很多关于制作人/消费者模式的知识,以及如何在.NET的BlockingCollection的帮助下实现它。

然而,我所看到的所有示例都假设消费者任务执行完成任何它需要对队列项执行的任务,然后立即转到下一个可用项。

在我的情况下,从队列中取出的项目将被呈现给用户进行操作/交互,直到他们表明他们已经完成,此时消费者可以自由地获取下一个可用项目。

我已经嘲笑了一个我想要做的事情的简单例子,希望能更好地说明它:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace BlockingCollectionTest
{
    public partial class Form1 : Form
    {
        internal class Job
        {
            public string Id;
            public string Data1;
            public string Data2;
            public string Data3;
        }

        BlockingCollection<Job> jobList = new BlockingCollection<Job>();
        int jobNumber;
        Job currentJob;

        Job incomingJob;

        Task workTask;
        TaskCompletionSource<bool> workDone;

        public Form1()
        {
            InitializeComponent();

           // Task option 1:
           // Can't access UI (easily)
           workTask = Task.Factory.StartNew(() => ProcessJobs());

           // Task option 2:
           // Blocks the UI thread while waiting for
           // BlockingCollection.Take()
           var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
           workTask = Task.Factory.StartNew(() => ProcessJobs(),
            CancellationToken.None, TaskCreationOptions.None, uiContext);

        }

        private async void ProcessJobs()
        {
            while (!jobList.IsAddingCompleted)
            {
                currentJob = jobList.Take();
                FillData();
                workDone = new TaskCompletionSource<bool>();
                await workDone.Task;
            }
        }

        private void btnCreateNewJob_Click(object sender, EventArgs e)
        {
            incomingJob = new Job();
            incomingJob.Id = jobNumber++.ToString();
        }

        private void btnObtainJobData_Click(object sender, EventArgs e)
        {
            if (incomingJob != null)
            {
                // Pretend this data isn't static
                incomingJob.Data1 = incomingJob.Id + " Data1";
                incomingJob.Data2 = incomingJob.Id + " Data2";
                incomingJob.Data3 = incomingJob.Id + " Data3";
                jobList.Add(incomingJob);
                incomingJob = null;
            }
        }

        private void FillData()
        {
            txtData1.Text = currentJob.Data1;
            txtData2.Text = currentJob.Data2;
            txtData3.Text = currentJob.Data3;
        }

        private void btnFinishCurrentJob_Click(object sender, EventArgs e)
        {
           // Do something with the edited data
            workDone.SetResult(true);
        }
    }
}

以下是表单可以帮助可视化此(设计)工作流程的内容: User Form 工作流程如下:

  1. 用户创建新职位
  2. 用户获取工作数据
  3. 作业已添加到作业列表
  4. ProcessJobs从列表中获取作业并填写表格
  5. 用户完成工作数据并完成后单击按钮
  6. 当操作员与作业数据交互时,允许他们创建其他作业并为每个作业获取数据而不会干扰当前作业。步骤1和2分开的原因是因为在实际应用中存在需要在获得作业数据之前完成的干预验证步骤。

    根据在Form1.Initialize()事件中创建任务的方式,会出现不同的问题。选项1意味着我无法轻松地与UI交互。我需要实现编组代码(如果这是最好的方法我将会这样做,但我觉得现代C#可能有更优雅的工具来处理它)。选项2导致UI线程阻塞,同时等待BlockingCollection从空队列中获取下一个作业,因此无法使用。

    我怀疑有更好的方法来实现这一点,这就是我在这里发帖的原因。是否有一个众所周知的生产者/消费者模式的变化将满足我的需求?或者可能是另一种模式?

0 个答案:

没有答案