条件が満たされたときに実行されるコードを記述しようとしています。現在、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には、編集モードでないときに発生するイベントハンドラーはありません。
少なくとも、ループをビジー待機からスローポーリングに変更できます。例えば:
while (!isExcelInteractive())
{
Console.WriteLine("Excel is busy");
await Task.Delay(25);
}
スレッド待機ハンドラ を使用できます
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();
}
今日これを書いてしまい、大丈夫のようです。あなたの使用法は次のとおりです。
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();
}
}
.netフレームワークで構築されている SpinUntil を使用できます。注:この方法ではCPU負荷が高くなります。
たくさんのことを掘った後、最終的に、私は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;
}
}