サードパーティのコンポーネントであるオブジェクトobj
があります。
// this could take more than 30 seconds
int result = obj.PerformInitTransaction();
私は内部で何が起こっているのか分かりません。私が知っているのは、時間がかかると失敗するということです。
この操作にタイムアウトメカニズムを設定する方法。30秒以上かかる場合は、MoreThan30SecondsException
?
別のスレッドで操作を実行してから、スレッド参加操作にタイムアウトを設定できます。
using System.Threading;
class Program {
static void DoSomething() {
try {
// your call here...
obj.PerformInitTransaction();
} catch (ThreadAbortException) {
// cleanup code, if needed...
}
}
public static void Main(params string[] args) {
Thread t = new Thread(DoSomething);
t.Start();
if (!t.Join(TimeSpan.FromSeconds(30))) {
t.Abort();
throw new Exception("More than 30 secs.");
}
}
}
より単純に Task.Wait(TimeSpan)
を使用:
using System.Threading.Tasks;
var task = Task.Run(() => obj.PerformInitTransaction());
if (task.Wait(TimeSpan.FromSeconds(30)))
return task.Result;
else
throw new Exception("Timed out");
メインスレッドをブロックしない場合は、 System.Threading.Timer を使用できます。
_private Thread _thread;
void Main(string[] args)
{
_thread = new ThreadStart(ThreadEntry);
_thread.Start();
Timer timer = new Timer(Timeout,null,30000,Timeout.Infinite);
}
void ThreadEntry()
{
int result = obj.PerformInitTransaction();
}
void TimeOut(object state)
{
// Abort the thread - see the comments
_thread.Abort();
throw new ItTimedOutException();
}
_
Jon Skeetには、中止よりも強制的にスレッドを停止する方法(ワーカースレッドを正常にシャットダウンする)があります。
ただし、PerformInitTransaction()
が実行している操作を制御していないため、Abortが失敗し、オブジェクトが無効な状態のままになったときからできることはあまりありません。前述のように、PerformInitTransaction
を中断したものを中断できるものをクリーンアップできる場合は、ThreadAbortException
をキャッチすることでこれを行うことができますが、サードパーティの呼び出しなので、状態を推測することを意味しますメソッドを残しました。
PerformInitTransaction
は、実際にタイムアウトを提供するものでなければなりません。
以下は、内部タスクで発生する例外もスローする2つの実装です。
アクションの場合(戻り値なし):
public static bool DoWithTimeout(Action action, int timeout)
{
Exception ex = null;
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
{
try
{
using (cts.Token.Register(Thread.CurrentThread.Abort))
{
action();
}
}
catch (Exception e)
{
if (!(e is ThreadAbortException))
ex = e;
}
}, cts.Token);
bool done = task.Wait(timeout);
if (ex != null)
throw ex;
if (!done)
cts.Cancel();
return done;
}
Funcsの場合(戻り値付き):
public static bool DoWithTimeout<T>(Func<T> func, int timeout, out T result)
{
Exception ex = null;
result = default(T);
T res = default(T);
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() =>
{
try
{
using (cts.Token.Register(Thread.CurrentThread.Abort))
{
res = func();
}
}
catch (Exception e)
{
if (!(e is ThreadAbortException))
ex = e;
}
}, cts.Token);
bool done = task.Wait(timeout);
if (ex != null)
throw ex;
if (done)
result = res;
else
cts.Cancel();
return done;
}
このような操作の中止には注意が必要です。特にサードパーティのコンポーネントにあるため、変更するコードにアクセスできない可能性があります。
操作を中止すると、基底クラスを残した状態がわかりません。たとえば、ロックを取得した可能性があり、aboutによってそのロックが解除されなかった可能性があります。操作を中止した後にオブジェクトを破棄した場合でも、オブジェクトに対してグローバルな状態が変更されている可能性があるため、再起動せずに新しいインスタンスを確実に作成することはできません。
スレッド内でメソッドを呼び出し、タイムアウト時にスレッドを中止し、例外を発生させることを確認できます。また、この場合、ThreadBorted例外を処理する必要があります。
これは私が使用するものです。 javascriptタイムアウトの仕組みと同様に機能します。
public class Toolz {
public static System.Threading.Tasks.Task<object> SetTimeout(Func<object> func, int secs) {
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(secs));
return System.Threading.Tasks.Task.Run(() => func());
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine(DateTime.Now);
Toolz.SetTimeout(() => {
Console.WriteLine(DateTime.Now);
return "";
}, 10);
}
}
ヘルパークラス here を使用したこれに対する一般的なソリューションの素敵な例があります。
アクションデリゲートを使用して、前の例で示したスレッドの作成/破棄を回避します。
これがお役に立てば幸いです。
.NET 4.0アプリでこれに遭遇しました(Task.Run
、Task.Delay
など)。最後の行(setTimeout部分)を言い訳する場合は、かなり簡潔です。
int sleepTime = 10000;
Action myAction = () => {
// my awesome cross-thread update code
this.BackColor = Color.Red;
};
new System.Threading.Thread(() => { System.Threading.Thread.Sleep(sleepTime); if (InvokeRequired) myAction(); else myAction(); }).Start();
私はこれが最も簡単だと思います:
using System.Threading.Tasks;
var timeToWait = 30000; //ms
Task.Run(async () =>
{
await Task.Delay(timeToWait);
//do your timed task i.e. --
int result = obj.PerformInitTransaction();
});