-1

次の基準を満たすメソッドを呼び出す必要があります。ハードウェアインタフェースのスレッド呼び出しにTask.Run()を使用する

  • このメソッドは数時間実行される可能性があります。
  • このメソッドは、ハードウェアとインターフェースできます。
  • このメソッドは、ユーザー入力(パラメーター値、確認など)を要求する場合があります。リクエストは、入力が受信されるまでメソッドをブロックする必要があります。

私は、次の設計を使用してこの基準を満たすプロトタイプの実装を行っています。

Formが存在し、Panelを含むものとします。

UserControlは、TextBoxButtonです。

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によってインスタンス化されます。 containerForm1で設定されたPanelで、呼び出し元のクラスに提供されます。

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コントローラクラスインスタンスInvokerForm1UserInputのインスタンスでRunTestAsync通過を呼び出します。

長時間実行されているブロックされているタスクや、ThreadPoolリソースの意味についてはいくつか懸念しています。しかし、一度に複数のメソッドを呼び出す機能は、私が構築しているアプリケーションでは提供されていません。呼び出されたメソッドが実行されている最中に、現在の要件でそのような機能を詳細に指定していない間に、アプリケーションがその他の限られた機能を提供する可能性があります。私は、いつでも1つの長時間稼動しているスレッドしか稼働していないことが予想されます。

このタイプのメソッド呼び出しにTask.Run()を使用するのは合理的な実装ですか?そうでない場合、必要な基準を提供するより合理的な実装は何でしょうか?私は、この呼び出しのためにThreadPoolの外に専用のスレッドを考慮する必要がありますか?

+0

ストップを使用すると、イベントハンドラを持っている場合、使用することが許容される唯一の時間は '非同期void'を行うと、戻り値の型は変更できません。 –

答えて

1

このタイプのメソッド呼び出しにTask.Run()を使用するのは合理的な実装ですか?あなたの「ハードウェアとのインタフェースは」のみ同期 APIを使用して行うことができると仮定すると

、[はい、Task.Runはそのための罰金です。

ただし、と呼ばれるとに変更されます。現在、Task.Runは、スレッドプールで実行されるasync voidメソッドをラップしています(Invokeを使用してUIスレッドに戻る)。これらはいずれも問題があります。がasync voidを超えると、「早い」(すなわち、最初のawait)を完了したように見えます。 Invokeを使用すると、タイトなカップリングが行われていることが示されます(UIは、UIを呼び出すバックグラウンドサービスを呼び出します)。

私はasync Taskasync voidを交換してもTask.RunInvokeを回避するために使用される場所を変更します:

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); 
+0

''非同期voidを介して '' Task.Run'が "早期"を完了するように見えるのはどういう意味ですか? 'await'は' async void'で別の働きをしますか? – OttPrime

+0

@OttPrime: 'async void'は"ハンドル "を返さないので、メソッドが完了すると、' Task.Run'はそのことを知ることができません。したがって、 'Task.Run'によって返されたタスクは、' async void'メソッド全体が完了したときではなく、最初の 'await'にヒットしたときに完了します。 –

+0

私はあなたの提案を実装し、私はあなたが呼び出しが必要でないことを意味するものを参照してください。しかし、私は複数の入力が必要なメソッドを持っています。その場合、2番目の 'await'ブロックは、すべての入力をブロックするのではなく最初の入力を受け取った直後に実行されます。私は 'AnotherMethodToInvoke'として上記の' Demo'クラスに例を追加しました。 'ui.GetInteger()'への2回目の呼び出しをブロックし続けるべきですか? – OttPrime

関連する問題