web-dev-qa-db-ja.com

C#条件が真になるまで待つ

条件が満たされたときに実行されるコードを記述しようとしています。現在、while ... loopを使用していますが、これはあまり効率的ではありません。 AutoResetEvent()も見ていますが、条件がtrueになるまでチェックを続けるように実装する方法がわかりません。

コードはたまたま非同期メソッド内に存在するため、何らかの待機が機能する可能性がありますか?

_private async void btnOk_Click(object sender, EventArgs e)
{
        // Do some work
        Task<string> task = Task.Run(() => GreatBigMethod());
        string GreatBigMethod = await task;

        // Wait until condition is false
        while (!isExcelInteractive())
        {
            Console.WriteLine("Excel is busy");
        }

        // Do work
        Console.WriteLine("YAY");
 }


    private bool isExcelInteractive()
    {
        try
        {
            Globals.ThisWorkbook.Application.Interactive = Globals.ThisWorkbook.Application.Interactive;
            return true; // Excel is free
        }
        catch
        {
            return false; // Excel will throw an exception, meaning its busy
        }
    }
_

CPUがループすることなくisExcelInteractive()をチェックし続ける方法を見つける必要があります。

:Excelには、編集モードでないときに発生するイベントハンドラーはありません。

23
Keylee

少なくとも、ループをビジー待機からスローポーリングに変更できます。例えば:

    while (!isExcelInteractive())
    {
        Console.WriteLine("Excel is busy");
        await Task.Delay(25);
    }
30
Ben Voigt

スレッド待機ハンドラ を使用できます

private readonly System.Threading.EventWaitHandle waitHandle = new System.Threading.AutoResetEvent(false);
private void btnOk_Click(object sender, EventArgs e)
{
    // Do some work
    Task<string> task = Task.Run(() => GreatBigMethod());
    string GreatBigMethod = await task;

    // Wait until condition is false
    waitHandle.WaitOne();
    Console.WriteLine("Excel is busy");
    waitHandle.Reset();

    // Do work
    Console.WriteLine("YAY");
 }

その後、他のジョブがハンドラを設定する必要があります

void isExcelInteractive()
{
   /// Do your check
   waitHandle.Set()
}

更新:このソリューションを使用する場合は、特定の間隔でisExcelInteractive()を連続して呼び出す必要があります。

var actions = new []{isExcelInteractive, () => Thread.Sleep(25)};
foreach (var action in actions)
{                                      
    action();
}
9
Peyman

今日これを書いてしまい、大丈夫のようです。あなたの使用法は次のとおりです。

await TaskEx.WaitUntil(isExcelInteractive);

コード(逆演算を含む)

public static class TaskEx
{
    /// <summary>
    /// Blocks while condition is true or timeout occurs.
    /// </summary>
    /// <param name="condition">The condition that will perpetuate the block.</param>
    /// <param name="frequency">The frequency at which the condition will be check, in milliseconds.</param>
    /// <param name="timeout">Timeout in milliseconds.</param>
    /// <exception cref="TimeoutException"></exception>
    /// <returns></returns>
    public static async Task WaitWhile(Func<bool> condition, int frequency = 25, int timeout = -1)
    {
        var waitTask = Task.Run(async () =>
        {
            while (condition()) await Task.Delay(frequency);
        });

        if(waitTask != await Task.WhenAny(waitTask, Task.Delay(timeout)))
            throw new TimeoutException();
    }

    /// <summary>
    /// Blocks until condition is true or timeout occurs.
    /// </summary>
    /// <param name="condition">The break condition.</param>
    /// <param name="frequency">The frequency at which the condition will be checked.</param>
    /// <param name="timeout">The timeout in milliseconds.</param>
    /// <returns></returns>
    public static async Task WaitUntil(Func<bool> condition, int frequency = 25, int timeout = -1)
    {
        var waitTask = Task.Run(async () =>
        {
            while (!condition()) await Task.Delay(frequency);
        });

        if (waitTask != await Task.WhenAny(waitTask, 
                Task.Delay(timeout))) 
            throw new TimeoutException();
    }
}

使用例: https://dotnetfiddle.net/Vy8GbV

3
Sinaesthetic

.netフレームワークで構築されている SpinUntil を使用できます。注:この方法ではCPU負荷が高くなります。

2
anion

たくさんのことを掘った後、最終的に、私はCIをハングさせない良い解決策を思いつきました:)それをあなたのニーズに合わせてください!

public static Task WaitUntil<T>(T elem, Func<T, bool> predicate, int seconds = 10)
{
    var tcs = new TaskCompletionSource<int>();
    using(var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)))
    {
        cancellationTokenSource.Token.Register(() =>
        {
            tcs.SetException(
                new TimeoutException($"Waiting predicate {predicate} for {elem.GetType()} timed out!"));
            tcs.TrySetCanceled();
        });

        while(!cancellationTokenSource.IsCancellationRequested)
        {
            try
            {
                if (!predicate(elem))
                {
                    continue;
                }
            }
            catch(Exception e)
            {
                tcs.TrySetException(e);
            }

            tcs.SetResult(0);
            break;
        }

        return tcs.Task;
    }
}
0
Oğuzhan Soykan