web-dev-qa-db-ja.com

非同期でx秒待機してから何かを実行する方法は?

C#およびWindowsフォームには Thread.Sleep およびSystem.Windows.Forms.Timerおよび Monitor.Wait があることを知っています。スレッドをロックせずに、X秒間待機してから何かを行う方法を理解できないようです。

ボタン付きのフォームがあります。ボタンをクリックすると、タイマーが開始し、5秒間待機します。この5秒後、フォーム上の他のコントロールの一部が緑色に変わります。 Thread.Sleepを使用すると、アプリケーション全体が5秒間応答しなくなります。「5秒後に何かを行う」にはどうすればよいですか。

23
Dennis G

(コメントはベンから転記)

system.Windows.Forms.Timerを使用するだけです。タイマーを5秒に設定し、Tickイベントを処理します。イベントが発生したら、処理を実行します。

...タイマーを無効(IsEnabled = false)にしてから、秒単位で抑制するように作業します。

Tickイベントmayは、GUIを変更できない別のスレッドで実行されます。これをキャッチできます:

private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();

    private void StartAsyncTimedWork()
    {
        myTimer.Interval = 5000;
        myTimer.Tick += new EventHandler(myTimer_Tick);
        myTimer.Start();
    }

    private void myTimer_Tick(object sender, EventArgs e)
    {
        if (this.InvokeRequired)
        {
            /* Not on UI thread, reenter there... */
            this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
        }
        else
        {
            lock (myTimer)
            {
                /* only work when this is no reentry while we are already working */
                if (this.myTimer.Enabled)
                {
                    this.myTimer.Stop();
                    this.doMyDelayedWork();
                    this.myTimer.Start(); /* optionally restart for periodic work */
                }
            }
        }
    }

完全を期すために:async/awaitを使用すると、非常に簡単なものを実行するのを遅らせることができます(ワンショット、呼び出しを繰り返さないでください)。

private async Task delayedWork()
{
    await Task.Delay(5000);
    this.doMyDelayedWork();
}

//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
    Task ignoredAwaitableResult = this.delayedWork();
}

詳細については、MSDNの「非同期で待機する」を参照してください。

35
eFloh

やってみました

public static Task Delay(
    int millisecondsDelay
)

次のように使用できます:

await Task.Delay(5000);

参照: https://msdn.Microsoft.com/en-us/library/hh194873(v = vs.110).aspx

13
macio.Jun

アクションを実行する非同期タスクを開始できます。

Task.Factory.StartNew(()=>
{
    Thread.Sleep(5000);
    form.Invoke(new Action(()=>DoSomething()));
});

[編集]

間隔を渡すには、変数に格納するだけです。

int interval = 5000;
Task.Factory.StartNew(()=>
{
    Thread.Sleep(interval);
    form.Invoke(new Action(()=>DoSomething()));
});

[/ EDIT]

10
PVitt

UIスレッドを希望どおりに待機することができます。

Task.Factory.StartNew(async() =>
{
    await Task.Delay(2000);

    // it only works in WPF
    Application.Current.Dispatcher.Invoke(() =>
    {
        // Do something on the UI thread.
    });
});
1
Masuri

メインUIスレッドで5秒のスリープ/待機を呼び出しているため、アプリケーションがハングします。 sleep/wait/whateverアクションを別のスレッドに入れ(実際にはSystem.Windows.Forms.Timerがそれを行うはずです)、それが完了すると、何らかのコントロールを緑色にするアクションを呼び出します。 InvokeRequiredを必ず確認してください。以下に短いサンプルを示します(SetTextは別のスレッドから呼び出すことができます。コールの場合、代わりに、テキストボックスがオンになっているメインUIスレッドで呼び出されます)。

private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{    
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
}
else
{
    this.textBox1.Text = text;
}
}

私は here からサンプルを取得しました(読む価値があります!)。

1
mtijn

あなたはそれを間違って見ています。ボタンをクリックすると、x秒間隔でタイマーが開始されます。それらが起動すると、イベントハンドラがタスクを実行します。

それで、あなたは何が起こりたくないのですか?.

X秒が経過している間。

タスクの実行中?

たとえば、遅延とタスクが完了するまでボタンをクリックしたくない場合などです。ボタンクリックハンドラーで無効にし、タスクの完了時に有効にします。

タスクの前に5秒の遅延が必要なだけの場合は、開始遅延をタスクに渡して、それを処理させる必要があります。

1
Tony Hopkinson

回答としてマークされた投稿の@eFlohは言った:

Tickイベントは、GUIを変更できない別のスレッドで実行される可能性があります。これをキャッチできます...

それはドキュメントが言うことではありません。
サンプルコードでSystem.Windows.Forms.Timerを使用しています。
これはForms.Timerです。
C#ドキュメントによると、タイマーイベントはUIスレッドで発生します。

このWindowsタイマーは、UIスレッドを使用して処理を実行するシングルスレッド環境向けに設計されています。これは、ユーザーコードに利用可能なUIメッセージポンプがあり、常に同じスレッドから動作する必要があります...

こちらもご覧ください stackoverflow post here

1
deegee

あなたが探しているのはSystem.Timers ... CWNDタイマーを設定する必要があるとは思わない:ご覧ください http://msdn.Microsoft.com/en-us/library/0tcs6ww8 (v = VS.90).aspx 例を示します。トリックを行う必要があります。

0
Ahmed Masud