1

オブジェクトの B メソッドで新しいスレッドを実行したいオブジェクト A があります。Task.CreateNew などを使用できます。問題は、新しいスレッドで例外を処理する方法がわからないことです。

一般的に私が望むのは、オブジェクトのBメソッドを持つ内部スレッドが例外をスローし、親オブジェクトAがその実行をキャッチして閉じることです.

  • マスターループにコードをまったく追加できません

  • マスターループが完了した後に例外をキャッチすることは受け入れられません。内部スレッドの例外が発生したときにキャッチしたい

それを達成する方法はありますか?

以下のコードでは、例外処理がなく、マスター スレッドが続行されます。

static void Main()
{
    Console.WriteLine("start");
    Task.Factory.StartNew(PrintTime, CancellationToken.None);

    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(1000);
    }
}

private static void PrintTime()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("inner thread i={0}",i+1);
        Thread.Sleep(1000);
    }

    throw new Exception("exception");
}
4

5 に答える 5

2

タスク インスタンスへの参照を保持しWait、結果を処理する準備ができたらそれを呼び出します。タスクの実行中にスローされた未処理の内部例外は、メソッドAggregateExceptionからスローされる にラップされます。Wait

static void Main()
{
    Console.WriteLine("start");
    Task task = Task.Factory.StartNew(PrintTime, CancellationToken.None);

    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(1000);

        // Stop iterating in case of unhandled exception in inner task.
        if (task.Status == TaskStatus.Faulted)
            break;
    }

    try
    {
        task.Wait();
    }
    catch (AggregateException ae) 
    {
        ae.Handle((x) =>
        {
             Console.WriteLine("Exception: " + x.ToString());
        });
    }
}
于 2013-09-30T10:36:00.043 に答える
1

以下の解決策は、どのスレッドが最初に例外をスローするかに関係なく機能します。

static void Main()
{
    Console.WriteLine("start");

    var innerCts = new CancellationTokenSource();
    Exception mainException = null;
    var mainThread = new Thread(() => SafeMainThread(innerCts, ref mainException));
    mainThread.Start();

    var innerTask = Task.Factory.StartNew(state => PrintTime(state),
                                          innerCts,
                                          innerCts.Token,
                                          TaskCreationOptions.LongRunning,
                                          TaskScheduler.Default);

    var innerFault = innerTask.ContinueWith(t => { Console.WriteLine("Inner thread caused " + t.Exception.InnerException.GetType().Name + ". Main thread is being aborted..."); mainThread.Abort(); },
                                            TaskContinuationOptions.OnlyOnFaulted);

    var innerCancelled = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread cancelled."),
                                                TaskContinuationOptions.OnlyOnCanceled);

    var innerSucceed = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread completed."),
                                              TaskContinuationOptions.OnlyOnRanToCompletion);

    try
    {
        innerTask.Wait();
    }
    catch (AggregateException)
    {
        // Ignore.
    }

    mainThread.Join();

    Console.ReadLine();
}

private static void SafeMainThread(CancellationTokenSource innerCts, ref Exception mainException)
{
    try
    {
        MainThread();
        Console.WriteLine("Main thread completed.");
    }
    catch (ThreadAbortException)
    {
        Console.WriteLine("Main thread aborted.");
    }
    catch (Exception exception)
    {
        Console.WriteLine("Main thread caused " + exception.GetType().Name + ". Inner task is being canceled...");

        innerCts.Cancel();
        mainException = exception;
    }
}

private static void MainThread()
{
    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(500);
    }
    throw new Exception("exception");
}

private static void PrintTime(object state)
{
    var cts = (CancellationTokenSource)state;

    for (int i = 0; i < 10; i++)
    {
        cts.Token.ThrowIfCancellationRequested();

        Console.WriteLine("inner thread i={0}", i + 1);
        Thread.Sleep(500);
    }

    throw new Exception("exception");
}
于 2013-09-30T14:01:51.483 に答える
0

本体の内部を変更できない場合、それをきめ細かく制御する方法がわかりません。現時点で私が見ている解決策は、内部スレッドが例外をスローした場合に本体を中止できるマネージド スレッドに本体をラップすることです。

static void Main()
{
    Console.WriteLine("start");

    var mainThread = new Thread(MainThread);
    mainThread.Start();

    var task = Task.Factory
                   .StartNew(PrintTime)
                   .ContinueWith(t => { Console.WriteLine("Inner thread caused exception. Main thread is being aborted."); mainThread.Abort(); },
                                 TaskContinuationOptions.OnlyOnFaulted);
    task.Wait();

    Console.WriteLine("Waiting for main thread to abort...");
    mainThread.Join();
    Console.WriteLine("Main thread aborted.");

    Console.ReadLine();
}

private static void MainThread()
{
    for (int i = 0; i < 20; i++)
    {
        Console.WriteLine("master thread i={0}", i + 1);
        Thread.Sleep(1000);
    }
}

private static void PrintTime()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("inner thread i={0}", i + 1);
        Thread.Sleep(1000);
    }

    throw new Exception("exception");
}
于 2013-09-30T12:10:38.933 に答える
0

例外の発生時に例外ハンドラーを試して実行できます。フラグを使用できます。例では、例外の発生自体をチェックするために例外を使用しました。

    private static AggregateException exception = null;

    static void Main(string[] args)
    {
        Console.WriteLine("Start");

        Task.Factory.StartNew(PrintTime, CancellationToken.None).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted);

        for (int i = 0; i < 20; i++)
        {
            Console.WriteLine("Master Thread i={0}", i + 1);
            Thread.Sleep(1000);
            if (exception != null)
            {
                break;
            }
        }

        Console.WriteLine("Finish");
        Console.ReadLine();
    }

    private static void HandleException(Task task)
    {
        exception = task.Exception;
        Console.WriteLine(exception);
    }

    private static void PrintTime()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Inner Thread i={0}", i + 1);
            Thread.Sleep(1000);
        }

        throw new Exception("exception");
    }
于 2013-09-30T11:10:53.043 に答える