【问题标题】:C# Task Return outputC# 任务返回输出
【发布时间】:2015-07-02 01:31:27
【问题描述】:

我已经阅读了很多关于如何为任务返回值的内容,但我似乎无法让它在我的代码上运行,并且仍然产生System.Threading.ThreadAbortException

尝试使用Task.WaitAll,尽管这可能会阻塞 UI,但无济于事。

public DataTable GetResult(SomeVariable someVariable) {
    // this do not work
    //var task = Task<DataTable>.Factory.StartNew(() =>
    var task = Task.Factory.StartNew<DataTable>(() =>
    {
        DataTable matchedData = new DataTable();
        matchedData = DoTask(someVariable);
        return matchedData;
    }, TaskCreationOptions.LongRunning);
    try
    {
        var allTasks = new Task[] { task };
        Task.WaitAll(allTasks);
        return task.Result as DataTable;
    }
    catch (ArgumentException)
    {
        throw;
    }
    catch (Exception)
    {
        // Always get the exception  here: "A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll"
        throw;
    }
}

尝试使用ContinueWhenAll,但还是一样。

public DataTable GetResultV2(SomeVariable someVariable)
{
    queue = new Queue<Task>();
    DataTable matchedData = new DataTable();
    var task = Task.Factory.StartNew(() =>
    {
        matchedData = DoTask(someVariable);
        return matchedData;
    }, TaskCreationOptions.LongRunning);
    queue.Enqueue(task);
    try
    {
        var done = Task.Factory.ContinueWhenAll(queue.ToArray(), completed =>
            {
                return matchedData;
            });
        return done.Result as DataTable;
    }
    catch (ArgumentException)
    {
        throw;
    }
    catch (Exception)
    {
        // Always get the exception  here: "A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll"
        throw;
    }
}

DoTask 只是一种检查和查询数据库的方法。

private DataTable DoTask(SomeVariable someVariable)
{
    DataTable matchedData = new DataTable();
    // long database process/query
    // populate and return matchedData
    return matchedData;
}

编辑:供参考如何/为什么使用它。



    foreach (DataRow row in data.Rows)
    {
        string columnName = Convert.ToString(row["columnName"]);
        string ProjectName = Convert.ToString(row["ProjectName"]);
        string dbase_group = Convert.ToString(row["dbase_group"]);
        string dbase_data = Convert.ToString(row["dbase_data"]);
        var task = Task.Factory.StartNew(() =>
            {
                SomeVariable someVariable = new SomeVariable();
                someVariable.DbName = dbase_group;
                someVariable.columnName = columnName;
                someVariable.ProjectName = ProjectName;
                someVariable.TblName = dbase_data;
                using (SearchProject search = new SearchProject())
                {
                    DataTable result = new DataTable();
                    result = search.GetResult(SomeVariable);
                }
            });
        queue.Enqueue(task);
    }
    Task.Factory.ContinueWhenAll(queue.ToArray(), ant =>
    {
        Console.WriteLine("Done with all tasks");
    });

【问题讨论】:

  • 为什么要在这种情况下使用任务?
  • 请参阅上面的更新代码。
  • 请将该代码添加到您的原始帖子中,使其格式正确,并且每个人都可以将其视为问题的一部分。
  • 请查看更新后的代码。
  • 我很确定您正在从外部中止线程。虽然这段代码是使用Tasks 的糟糕方法,但没有机会从内部使用Thread.Abort。尝试在整个代码库中搜索Thread.Abort。此外,您确实意识到您永远不会返回 result,对吧?

标签: c# .net multithreading task


【解决方案1】:

我认为是时候让您迈出异步/等待的步骤了。这将使您的生活更轻松。

您希望在代码中的某个位置启动多个任务并等待所有任务完成而不会阻塞您的用户界面。在您的示例中,这是 GetResult。

您希望 GetResult 返回一个 DataTable 对象。如果要使用异步等待,则声明函数 GetResult 并返回一个 Task,如下所示:

public async Task<DataTable> GetResultAsync(SomeVariable someVariable) {...}

用 async 来命名你的异步函数是很常见的

在此功能中,您可以启动任务,在这些任务运行时执行其他操作并等待任务完成。这种等待称为“等待”。

你只能等待一个任务或一个任务对象,所以你等待一个返回任务的函数。

Task.WaitAll 不返回任务,而是无效。所以你不能等待 Task.Waital。

更好的是等待Task.WhenAll。该函数返回一个任务,因此您可以等待它。

public async Task<DataTable> GetResultAsync(SomeVariable someVariable)
{
    var task = Task.Run( () =>
    {
        DataTable matchedData = new DataTable();
        matchedData = DoTask(someVariable);
        return matchedData;
    }
}

如果您仍然可以使用 Task.Factory.StartNew,请参阅 MSDN,了解他们现在更喜欢 Task.Run 的原因

此功能将为您提供一个结果。如果要调用 if,则必须使调用者函数也异步并让它返回 Task 或 Task。它的调用者也应该是异步的等,直到你到达事件处理程序。这是唯一可能返回 void 的人:

private async void OnButton1_clicke(object Sender, ...)
{
    try
    {
        await ProcessAllInputsAsync(...)
    }
    catch (ArgumentException exc)
    {
        ProcessArgumentException(...)
    }
    catch (Exception exc)
    {
         ProcessOtherException(...)
    }           
}

// first example: no parallel processing:
private async Task ProcessAllInputsAsync(...)
{   
    foreach (SomeVariable someVariable in GetSomeVariables(...))
    {
        DataTable dataTable = await GetResultAsync(...);
        ProcessDataTable(dataTable);
    }
}

// or do parallel processing: start several tasks and wait until all ready:
private async Task ProcessAllInputsAsync(...)
{ 
    List<Task<DataTable>> tasks = new List<Task<DataTable>>();  
    foreach (SomeVariable someVariable in GetSomeVariables(...))
    {
        tasks.Add(GetResultAsync(someVariable);
    }
    // all tasks are started, await for all to finish:
    await Task.WhenAll(tasks.ToArray());
    // REMEMBER: you can only await for a Task or Task<T>
    // Task.WhenAll returns a Task, so you can await for it
    // Task.WaitAll returns void, so you can't await for it.

    // now that all tasks are finished, get the results:
    // Each Task<TResult> has the result in property Task.Result
    // The result of a Task<TResult> is a TResult:
    IEnumerable<TResult> fetchedDataTables = tasks.Select(task => task.Result);

    // you can process it here if you want or do other things with it:
    foreach (DataTabe fetchedDataTable in fetchedDataTables)
    {
        ProcessFetchedDataTable(fetchedDataTable);
    }
}

看到你摆脱了所有 ContinueWith 等东西。它被 await 替换,然后是下一条语句,其中任务的结果在 Task.Result 中可用。

请注意:如果您执行 Task.WhenAll,并且您正在等待的任务之一引发异常,您会得到一个 AggregateException,其中所有任务引发的所有异常都分组在属性 InnerExceptions 中。因此,如果您愿意,您可以捕获 AggregateException 并 foreach 所有内部异常,以查看哪个任务引发了哪些异常。

【讨论】:

  • 您的 GetResultAsync() async 方法无法编译并给出错误:Not all code paths return a value(我意识到这是几年后的事了,虽然这似乎是一个很好的答案,但只需要一些调整)跨度>
猜你喜欢
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 1970-01-01
  • 1970-01-01
  • 2012-02-04
  • 2021-08-05
  • 1970-01-01
相关资源
最近更新 更多