4

次のようなタスクを返すメソッドがあります。

public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
    if (socket == null) throw new ArgumentNullException("socket");
    if (buffer == null) throw new ArgumentNullException("buffer");

    return Task.Factory.FromAsync<int>(
        socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
        socket.EndSend);
}

タスクへの参照を保持し、後で実行したいと思います。ただし、FromAsyncメソッドによって作成されたタスクはすぐに実行されるようです。どうすれば実行を延期できますか?

4

3 に答える 3

5

インターフェイスでタスクを返す必要がある場合は、BeginSend()呼び出しを行うためだけにスレッドプールでの作業を不必要にスケジュールすることになります(これは、FromAsync()呼び出しが別のタスクによってラップされている前の回答で起こっていることです)。

代わりに、インターフェースを変更できる場合は、次のような標準の遅延実行手法を使用できます。

public static Func<Task<int>> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
    if (socket == null) throw new ArgumentNullException("socket");
    if (buffer == null) throw new ArgumentNullException("buffer");
    return () => 
        Task.Factory.FromAsync<int>(
            socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
            socket.EndSend);
}

呼び出し元は結果を呼び出して操作を開始し(つまり、FromAsync / BeginSendを呼び出し)、ContinueWith()を使用して結果を処理します。

Func<Task<int>> sendAsync = socket.SendAsync(buffer, offset, count);
sendAsync().ContinueWith(
    antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));

インターフェイスでFunc<>を公開することが適切でない場合は、別のクラスにラップすることができます。

public class DelayedTask<TResult>
{
    private readonly Func<Task<TResult>> func;

    public DelayedTask(Func<Task<TResult>> func)
    {
        this.func = func;
    }

    public Task<TResult> Start()
    {
        return this.func();
    }
}

public static DelayedTask<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
    if (socket == null) throw new ArgumentNullException("socket");
    if (buffer == null) throw new ArgumentNullException("buffer");
    return new DelayedTask<int>(() =>
        Task.Factory.FromAsync<int>(
            socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
            socket.EndSend));
}

そして、発信者は次のようになります。

DelayedTask<int> task = socket.SendAsync(buffer, offset, count);
task.Start().ContinueWith(
    antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));
于 2011-05-14T22:09:19.420 に答える
1

手始めに、構文を見ると、実際にBeginSend自分でメソッドを呼び出してIAsyncResult、FromAsyncの最初のパラメーターのを返すようになっていることがわかります。ただし、これはFromAsyncのオーバーロードの1つにすぎません。あなたが見るならば、他の過負荷があり、あなたが探しているものはFunc<...>代わりに取るものです。残念ながら、これらのオーバーロードは、内部で、FromAsyncが。を使用してAPM呼び出しパターンをラップしているだけであるという事実のために、ユーザーに代わってすぐにメソッドを呼び出しますTaskCompletionSource<TResult>

実行を延期できることを実際に確認できる唯一の方法は、自分FromAsyncではない親タスクで作業を実際にラップすることですStart。例えば:

public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
    if (socket == null) throw new ArgumentNullException("socket");
    if (buffer == null) throw new ArgumentNullException("buffer");

    return new Task<int>(() =>
    {
        return Task.Factory.FromAsync<int>(
                         socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
                         socket.EndSend).Result;
    }
}

これで、発信者は次のようなタスクを取得できます。

Task<int> task = SendAsync(...);

そして彼らが呼ぶまでtask.Start()仕事は始まらないでしょう。

于 2010-10-30T05:09:09.250 に答える
0

タスクを別のタスクでラップすると、実行が延期される可能性があります。

  // Wrap the task
  var myTask = new Task<Task<int>>(() => SendAsync(...));

  // Defered start
  Thread.Sleep(1000); 
  myTask.Start();
  // Thread is not blocked
  Console.WriteLine("Started");

  // Wait for the result
  Console.WriteLine(myTask.Unwrap().Result);
于 2011-12-11T17:33:51.137 に答える