C# 将方法执行移动到单独的线程并接收返回值

C# 将方法执行移动到单独的线程并接收返回值,c#,multithreading,winforms,C#,Multithreading,Winforms,我对多线程之类的技术还比较陌生,但我仍在试图对整个过程了如指掌。 我有以下场景(简化): 这很奇怪,但由于逻辑收集数据的部分需要几秒钟,因此表单将始终冻结。我不想这样,所以我现在正试图摆脱它。我尝试的是稍微更改GetData()调用: Task<TabControl> t = Task<TabControl>.Factory.StartNew(GetData); tabControl1 = t.Result; Task t=Task.Factory.StartNew(G

我对多线程之类的技术还比较陌生,但我仍在试图对整个过程了如指掌。
我有以下场景(简化):

这很奇怪,但由于
逻辑
收集数据的部分需要几秒钟,因此表单将始终冻结。我不想这样,所以我现在正试图摆脱它。我尝试的是稍微更改
GetData()
调用:

Task<TabControl> t = Task<TabControl>.Factory.StartNew(GetData);
tabControl1 = t.Result;
Task t=Task.Factory.StartNew(GetData);
tabControl1=t.结果;
当然,我相应地调整了
GetData()
,因此它现在返回一个新的
TabControl
,而不是直接访问表单。
但是,这并没有改善我的情况,这可能是因为访问任务的
Result
属性迫使访问任务等待完成。

因此,我目前正在寻找一种不同的方法来实现这一点,但我想不出任何办法

我想最好的方法是使用ContinueWith:

t.ContinueWith(formerTask => {
    if (formerTask.IsFaulted) return;
    var x = formerTask.Result;
    // do whatever but use Invoke if necessary
})

这将在任务完成后启动任务以处理结果。

我想最好的方法是使用ContinueWith:

t.ContinueWith(formerTask => {
    if (formerTask.IsFaulted) return;
    var x = formerTask.Result;
    // do whatever but use Invoke if necessary
})

这将启动一个任务,以便在任务完成后处理结果。

另一种处理方法是使eventhandler
异步:

private async void GetMeSomeDataToolStripMenuItem_Click(object sender, EventArgs e)
{
   TabControl t = await Task<TabControl>.Run(GetData);
   tabControl1 = t;
}
private async void GetMeSomeDataToolStripMenuItem\u单击(对象发送方,事件参数e)
{
TabControl t=wait Task.Run(GetData);
tabControl1=t;
}
请注意,
async void
模式通常应该避免,但对于EventHandler是可以的

当您需要使用返回的结果更新GUI时,这种方法会容易得多。但是,在另一个线程上创建TabControl非常可疑。您应该将数据获取和控件创建分开。后一个动作应该在主线程上执行


这将解冻您的GUI,但为了使其真正有效,GetData方法应设置为异步,而task.Run则由等待I/O方法代替。

另一种处理方法是使eventhandler
异步。

private async void GetMeSomeDataToolStripMenuItem_Click(object sender, EventArgs e)
{
   TabControl t = await Task<TabControl>.Run(GetData);
   tabControl1 = t;
}
private async void GetMeSomeDataToolStripMenuItem\u单击(对象发送方,事件参数e)
{
TabControl t=wait Task.Run(GetData);
tabControl1=t;
}
请注意,
async void
模式通常应该避免,但对于EventHandler是可以的

当您需要使用返回的结果更新GUI时,这种方法会容易得多。但是,在另一个线程上创建TabControl非常可疑。您应该将数据获取和控件创建分开。后一个动作应该在主线程上执行


这将解冻您的GUI,但为了使其真正有效,GetData方法应该是异步的,而task.Run被替换为等待I/O方法。

好吧,经过一些工作后,我发现dryman的答案对我来说不是最优的。这就是我所做的:

private async void GetData()
{    
    tabControl1.TabPages.Clear();

    Task<List<string>> dataSetNames = Task<List<string>>.Factory.StartNew(logic.GetDataSetNames);

    await dataSetNames;

    foreach (var dataSetName in dataSetNames.Result)
    {
        Task<DataTable> sourceTable = Task<DataTable>.Factory.StartNew(() => logic.GetDataSet(dataSetName));

        TabPage page = new TabPage { Name = dataSetName }
        DataGridView dgv = new DataGridView { Dock = DockStyle.Fill }

        dgv.DataSource = await sourceTable;

        page.Controls.Add(dgv);
        tabControl1.TabPages.Add(page);
    }
}
private async void GetData()
{    
tabControl1.TabPages.Clear();
Task dataSetNames=Task.Factory.StartNew(logic.GetDataSetNames);
等待数据集名称;
foreach(dataSetNames.Result中的var dataSetName)
{
Task sourceTable=Task.Factory.StartNew(()=>logic.GetDataSet(dataSetName));
TabPage page=new TabPage{Name=dataSetName}
DataGridView dgv=new DataGridView{Dock=DockStyle.Fill}
dgv.DataSource=await sourceTable;
页面控件添加(dgv);
tabControl1.TabPages.Add(第页);
}
}
这甚至使我的代码更加枯燥,因为我不必写

Task<TabControl> t = Task<TabControl>.Factory.StartNew(GetData);
Task t=Task.Factory.StartNew(GetData);
每次我想调用GetData()。


TL;DR:使用
等待threadName

好吧,经过一番努力,我发现dryman的答案并不适合我。这就是我所做的:

private async void GetData()
{    
    tabControl1.TabPages.Clear();

    Task<List<string>> dataSetNames = Task<List<string>>.Factory.StartNew(logic.GetDataSetNames);

    await dataSetNames;

    foreach (var dataSetName in dataSetNames.Result)
    {
        Task<DataTable> sourceTable = Task<DataTable>.Factory.StartNew(() => logic.GetDataSet(dataSetName));

        TabPage page = new TabPage { Name = dataSetName }
        DataGridView dgv = new DataGridView { Dock = DockStyle.Fill }

        dgv.DataSource = await sourceTable;

        page.Controls.Add(dgv);
        tabControl1.TabPages.Add(page);
    }
}
private async void GetData()
{    
tabControl1.TabPages.Clear();
Task dataSetNames=Task.Factory.StartNew(logic.GetDataSetNames);
等待数据集名称;
foreach(dataSetNames.Result中的var dataSetName)
{
Task sourceTable=Task.Factory.StartNew(()=>logic.GetDataSet(dataSetName));
TabPage page=new TabPage{Name=dataSetName}
DataGridView dgv=new DataGridView{Dock=DockStyle.Fill}
dgv.DataSource=await sourceTable;
页面控件添加(dgv);
tabControl1.TabPages.Add(第页);
}
}
这甚至使我的代码更加枯燥,因为我不必写

Task<TabControl> t = Task<TabControl>.Factory.StartNew(GetData);
Task t=Task.Factory.StartNew(GetData);
每次我想调用GetData()。


TL;DR:使用
等待threadName

控件不是不能在非UI线程上初始化吗?改变了吗?它可以创建,但实际上不能“初始化”。我认为它只在线程不需要HWND句柄的情况下工作。这给了我一个语法错误。以下方法或属性之间的调用不明确:“Task.Run(Func)”和“Task.Run(Action)”我认为您必须再次调整GetData()。发布的代码似乎有点不一致(它返回的是void还是TabControl?),不是控件无法在非UI线程上初始化的情况吗?改变了吗?它可以创建,但实际上不能“初始化”。我认为它只在线程不需要HWND句柄的情况下工作。这给了我一个语法错误。以下方法或属性之间的调用不明确:“Task.Run(Func)”和“Task.Run(Action)”我认为您必须再次调整GetData()。发布的代码似乎有点不一致(返回void还是TabControl?)