使用Task.Run()进行硬件接口线程调用

时间:2016-05-19 13:07:06

标签: c# multithreading async-await task-parallel-library .net-4.5

我需要调用符合以下条件的方法。

  • 该方法可能会持续数小时。
  • 该方法可以与硬件接口。
  • 该方法可以请求用户输入(参数值,确认等)。请求应该阻止该方法,直到收到输入。

我有一个原型实现,使用以下设计来满足此标准。

假设存在Form且包含Panel

IntegerInput课程为UserControlTextBoxButton

public partial class IntegerInput : UserControl
{
  public TaskCompletionSource<int> InputVal = new TaskCompletionSource<int>(0);

  public IntegerInput()
  {
    InitializeComponent();
  }

  private void button1_Click(object sender, EventArgs e)
  {
    int val = 0;

    Int32.TryParse(textBox1.Text, out val);

    InputVal.SetResult(val);
  }
}

Form1UserInput类由Form1实例化。在提供给调用类之前,container是由Panel设置的Form1

public interface IUserInput
{
  Task<int> GetInteger();
}

public class Form1UserInput : IUserInput
{
  public Control container;

  private IntegerInput integerInput = new IntegerInput();
  public IntegerInput IntegerInput { get { return integerInput; } }

  public async Task<int> GetInteger()
  {
    container.Invoke(new Action(() => 
      { 
        container.Controls.Clear();
        container.Controls.Add(integerInput); 
      }));

    await integerInput.InputVal.Task;

    return integerInput.InputVal.Task.Result;
  }
}

Demo类包含我想要调用的方法。

public class Demo
{
  public IUserInput ui;

  public async void MethodToInvoke()
  {
    // Interface with hardware...

    // Block waiting on input
    int val = await ui.GetInteger(); 

    // Interface with hardware some more...
  }

  public async void AnotherMethodToInvoke()
  {
    // Interface with hardware...

    // Block waiting on multiple input
    int val1 = await ui.getInteger();
    int val2 = await ui.getInteger();

    // Interface with hardware...
  }
}

这是调用类的外观的大致轮廓。对Task.Run()的调用对我的原型来说是准确的。

public class Invoker
{
  public async Task RunTestAsync(IUserInput ui)
  {
    object DemoInstance = Activator.CreateInstance(typeof(Demo));
    MethodInfo method = typeof(Demo).GetMethod("MethodToInvoke");
    object[] args = null;

    ((IUserInput)DemoInstance).ui = ui;

    var t = await Task.Run(() => method.Invoke(DemoInstance, args));

    // Report completion information back to Form1
  }
}

Form1控制器类实例Invoker并调用传递RunTestAsync实例的Form1UserInput

我知道可能会阻止长期运行的任务以及对ThreadPool资源意味着什么的一些问题。但是,我正在构建的应用程序不提供一次调用多个方法的能力。当被调用的方法正在运行时,应用程序可能提供一些其他有限的功能,但当前的要求没有详细指定这样的功能。我预计在任何时候都只会有一个长时间运行的线程。

是否使用Task.Run()为这种类型的方法调用合理的实现?如果没有,那么提供所需标准的更合理的实施是什么?我应该考虑ThreadPool之外的专用线程来进行此调用吗?

1 个答案:

答案 0 :(得分:1)

  

对这种类型的方法调用使用Task.Run()是否合理实现?

假设您的&#34;界面与硬件&#34;只能使用同步 API来完成,然后是的,Task.Run就可以了。

但是,当被调用时,我会更改。现在,Task.Run正在包装一个在线程池上执行的async void方法(并使用Invoke跳回UI线程)。这些都是有问题的:Task.Run超过async void似乎会完成&#34;早期&#34; (即在第一个await);使用Invoke表示正在进行紧密耦合(UI调用后台服务调用UI)。

我会将async void替换为async Task并更改Task.Run用于避免Invoke的位置:

public async Task<int> GetInteger()
{
  container.Controls.Clear();
  container.Controls.Add(integerInput); 

  // Note: not `Result`, which will wrap exceptions.
  return await integerInput.InputVal.Task;
}


public async Task MethodToInvokeAsync()
{
  await Task.Run(...); // Interface with hardware...

  // Block waiting on input
  int val = await ui.GetInteger(); 

  await Task.Run(...); // Interface with hardware some more...
}


var t = await (Task)method.Invoke(DemoInstance, args);