6

Refresh という名前の非同期操作があります。最初の更新が完了する前に 2 回目の更新呼び出しが行われた場合は、それをキューに入れる必要があります。これは私が持っているものです:

public async Task Refresh(RefreshArgs refreshArgs)
{
    await EnqueueRefreshTask(refreshArgs);
}

private Queue<RefreshArgs> refreshQueue =
    new Queue<RefreshArgs>();

private async Task EnqueueRefreshTask(RefreshArgs refreshArgs)
{
    refreshQueue.Enqueue(refreshArgs);

    await ProcessRefreshQueue();
}

private Task currentRefreshTask = null;

private async Task ProcessRefreshQueue()
{
    if ((currentRefreshTask == null) || (currentRefreshTask.IsCompleted))
    {
        if (refreshQueue.Count > 0)
        {
            var refreshArgs = refreshQueue.Dequeue();

            currentRefreshTask = DoRefresh(refreshArgs);
            await currentRefreshTask;

            await ProcessRefreshQueue();
        }           
    }
}

private async Task DoRefresh(RefreshArgs refreshArgs)
{
    // Lots of code here, including calls to a server that are executed with await.
    // Code outside my control may make another Refresh(args) call while this one is still processing.
    // I need this one to finish before processing the next.
}

動作しますが、Tasks でこれを行うのが最善の方法かどうかはわかりません。何かご意見は?

アップデート:

私はActionBlockを使用してみました:

public async Task Refresh(RefreshArgs refreshArgs)
{
    if (refreshActionBlock == null)
    {
        var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions();
        executionDataflowBlockOptions.MaxMessagesPerTask = 1;
        executionDataflowBlockOptions.TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();

        refreshActionBlock = new ActionBlock<RefreshArgs>(args => DoRefresh(args), executionDataflowBlockOptions);
    }

    await refreshActionBlock.SendAsync(refreshArgs);
}

これにより、DoRefresh がキューに入れられ、UI スレッド (これが必要です) で実行できるようになります。問題は、SendAsync が DoRefresh の作業を待機しないことです。

SendAsync: "ターゲット メッセージ ブロックに非同期的にメッセージを提供し、延期を可能にします". アクション自体ではなく、送信を待っているだけです。

これを行うと、期待どおりに機能しません。

await Refresh(RefreshArgs.Something);
// other code goes here. It expects the first refresh to be finished.
await Refresh(RefreshArgs.SomethingElse);
// other code goes here. It expects the second refresh to be finished.

ActionBlock は 2 番目の更新をキューに入れますが、更新が完了する前に待機が失敗します。DoRefresh の作業が完了したら、それらを返す必要があります。

4

3 に答える 3

3

これを行う最も簡単な方法は、を使用することだと思いますAsyncLockStephen Cleary のライブラリ AsyncEx から入手するか、 Stephen Toub の記事 (自分でビルドする方法) を読むことができます。

があればAsyncLock、実装Refresh()は簡単です。

public async Task Refresh(RefreshArgs refreshArgs)
{
    using (await m_lock.LockAsync())
    {
        // do your async work here
    }
}

これにより、Refresh()es が次々に実行され (インターリーブされず)、Task返された fromが実際に実行されRefresh()た後にのみ完了するようになります。Refresh()

TPL Dataflow を使用して同じことを行うこともできますが、使用する必要があり、バージョンよりもはるかに複雑になります。ActionBlockTaskCompletionSourceAsyncLock

于 2013-04-10T19:25:11.753 に答える
2

これらの投稿を見ると、タスクを使用した非同期調整 (セマフォ、リセット イベント) と除外について説明しています。

[編集:パート6を追加]

于 2013-04-10T15:33:55.143 に答える
0

これはとても簡単です:) 「switch - case」を使用する必要があるのはあなただけです!! それでは、小さなサンプル コードを使用する方法を示しましょう。サンプル シナリオ: 2 つの pic をキューとして次々にダウンロードします。

名前空間テスト {

public partial class SampleOfQueue : Form
{

    public SampleOfQueue() { InitializeComponent(); }
    DBContext db = new DBContext();
    int Queue;

    private void btnStartApp_Click(object sender, EventArgs e)
    {
        Queue = 0;
        DownloadQueue();
    }

    private void DownloadQueue()
    {
        switch (Queue)
        {
            case 0:
                {
                    DownloadFile("http://images.forbes.com/media/lists/53/2009/tom-cruise.jpg", "Tom Cruise 1", "");
                    Queue += 1; break;
                }
            case 1:
                {
                    DownloadFile("https://upload.wikimedia.org/wikipedia/commons/6/69/Tom_Cruise_Collateral.jpg", "Tom Cruise 2", "");
                    Queue += 1; break;
                }
            case 2:
                {
                    // Other....
                    Queue += 1; break;
                }
            default: break;
        }

    }

    public void DownloadFile(string urlAddress, string ImageName, string ImageFarsiName)
    {
        WebClient webClient = new WebClient();
        webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);

        Uri URL = urlAddress.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ? new Uri(urlAddress) : new Uri(urlAddress);
        webClient.DownloadFileAsync(URL, "d:\\x.jpg");

    }

    private void Completed(object sender, AsyncCompletedEventArgs e)
    {
        DownloadQueue();
        if (Queue == 3) { MessageBox.Show("finish"); }
    }

}

}

VS 2015 Windows フォームの C# 4.6 でテスト済み。私自身の革新的な方法 :) 役立つことを願っています <3 頑張ってください。

于 2015-12-25T09:56:35.400 に答える