8

Active Directory から取得している PC のリストに対して並列操作を実行しています。この方法を使用して、コンピューターがオンラインかどうか、特定のディレクトリが存在するかどうかなど、PC の状態を確認していました。ただし、これらの操作の性質上、場合によっては時間がかかるため、タイムアウトを含めて、アプリケーションが引き続き操作できるようにしたいと考えました。

public static T MethodTimeout<T>(Func<T> f, int timeout, out bool completed)
{
    T result = default(T);
    var thread = new Thread(() => result = F());
    thread.Start();
    Completed = thread.Join(Timeout);
    if (!Completed) thread.Abort();
    return result;
}

これはほとんどの場合機能しますが、処理の使用量が少し急増したように見え、いくつかのケースではメモリ不足の例外に遭遇しました。ThreadPoolそのため、前述の問題が解消されることを期待して、タスクを使用する方法を変更しました。

public static T MethodTimeout<T>(Func<T> f, int timeout, out bool completed)
{
    T result = default(T);

    var timedTask = Task.Factory.StartNew(() => result = F());
    Completed = timedTask.Wait(Timeout);

    return result;
}

ThreadPoolただし、これらの潜在的に長いタスクが完了するのを待ってハングしているプロセスでいっぱいになっているような気がします。タスクの関数をパラメーターとして渡しているため、キャンセル トークンを使用する方法がわかりません。ただし、これらのクラスでの私の経験は非常に限られているため、いくつかの優れたテクニックを見逃している可能性があります.

上記の方法を使用した方法は次のとおりです。

bool isReachable; // Check if the directory exists (1 second)
MethodTimeout(() => Directory.Exists(startDirectory), 1000, out isReachable);

簡単に言うと、上記のチェックは、PC が WMI 呼び出し (これも を使用MethodTimeout) を介してオンラインであることを確認した後にのみ実行しています。初期のテストで、それが非効率になる前にディレクトリをチェックすることを認識しています。

また、このアプローチをより良いものに置き換えることにもオープンです。

4

3 に答える 3

6

私はここで悪いニュースの前触れかもしれませんが、このシナリオに対処するのはほとんどの人が考えているよりもはるかに困難です. あなたはすでにこれに取り組んでいるようです。協調的なキャンセル メカニズムを使用することはすべて良いことですが、それらを効果的に使用するには、時間のかかる操作を実際にキャンセルできる必要があります。Directory.Existsキャンセルできないのが難点。

最初の解決策の問題は、スレッドを中止していることです。これは、予測できない時点でスレッドを停止させるため、お勧めできません。これにより、アボートが挿入されたときにコール スタックで実行されていたすべてのデータ構造が破損する可能性があります。Thread.Abortこの特定のケースでは、通話が実際にハングしても驚かないでしょう。その理由は、実行がアンマネージ コードで行われている間は、通常、中止が遅延されるためです。Directory.Existsアンマネージ モジュールに依存する可能性があります。その場合、アボートはとにかく機能しません。

2 番目の解決策の問題は、タスクを孤立させたままにしておくことです。そのDirectory.Exists呼び出しは、どこかのスレッド プール スレッドで引き続き実行されます。これは、実際にキャンセルしていないためです。

正直なところ、これについてどうすればよいかよくわかりません。キャンセル可能Directory.Existsなメソッドがないことは非常に問題です。私の最初の考えは、その存在を確認するための一種のプロキシとして、テストしたいディレクトリから書き込みまたは読み取りを試みることです。FileStreamクラスにはキャンセル可能な操作があります。実際、メソッドの多くはCancellationTokena をパラメーターとして受け入れます。FileStreamまたは、保留中の操作をキャンセルする を閉じることもできます。もう 1 つのオプションは、Win32 API 関数を使用して IO を実行することです。その後、必要に応じてCancelSynchronousIoを呼び出すことができます。

私が実際に行ったのは、あなたができないことを伝えただけであり、決定的な解決策を提供しなかったためです。ポイントは、最善の解決策はキャンセル可能な操作から始まるということです。残念なことに、一部の BCL クラスはこれらを提供する必要がある場合でも提供していません。

于 2013-11-01T02:50:14.693 に答える
4

.Net 4.5 を使用している場合は、次を使用して実行できますCancelAfter

var cts = new CancellationTokenSource(3000); // Set timeout

Task.Run(() =>
{
    while (!cts.Token.IsCancellationRequested)
    {
        // Doing Work...
    }

}, cts.Token);
于 2013-10-31T23:50:55.110 に答える
0

このようなものは機能しますか?消費者が を受け取る を渡すことを要求する必要がありFuncますCancellationTokenSource。それFuncは、そのコードの適切な場所でチェックする責任がIsCancellationRequestedあります...

public static T MethodTimeout<T>(Func<T, CancellationTokenSource> F, 
                                 int Timeout, out bool Completed)
{
    T result = default(T);
    var source = new CancellationTokenSource(Timeout);
    var timedTask = Task.Factory.StartNew(() => result = F(source));
    Completed = timedTask.Wait(Timeout);
    if(!Completed) source.Cancel();
    return result;
}

私の TPL に関する知識はほとんどないため、私のコードが正しくない場合は申し訳ありません。古いバージョンの VS でテストする方法がありません。修正/編集歓迎:)

于 2013-11-01T00:11:12.523 に答える