在新线程上运行简单函数的最佳方法是什么?

36

我有两个函数希望在不同线程上运行(因为它们是数据库操作,而且不需要立即执行)。

这两个函数如下:

            getTenantReciept_UnitTableAdapter1.Fill(rentalEaseDataSet1.GetTenantReciept_Unit);
            getTenantReciept_TenantNameTableAdapter1.Fill(rentalEaseDataSet1.GetTenantReciept_TenantName);
在JavaScript中,我知道可以创建一个匿名函数并使用类似以下代码在新线程上调用它:
setTimeout(new function(){doSomethingImportantInBackground();}, 500);

在C#中有类似这样的东西吗?


2
只是顺便提一下,那段Javascript代码并没有在新线程上调用函数,它只是将其安排在未来运行。Javascript是单线程的,因此所有代码都在同一个线程上运行。 - BlueRaja - Danny Pflughoeft
4个回答

77

很抱歉,您的问题不是很清楚。您可以使用C# 2中的匿名方法或C# 3中的lambda表达式来编写一些代码,并轻松地启动一个新线程:

匿名方法:

new Thread(delegate() {
    getTenantReciept_UnitTableAdapter1.Fill(
        rentalEaseDataSet1.GetTenantReciept_Unit);
}).Start();
new Thread(delegate() {
    getTenantReciept_TenantNameTableAdapter1.Fill(
        rentalEaseDataSet1.GetTenantReciept_TenantName);
}).Start();

Lambda表达式:

new Thread(() =>
    getTenantReciept_UnitTableAdapter1.Fill(
        rentalEaseDataSet1.GetTenantReciept_Unit)
).Start();
new Thread(() =>
    getTenantReciept_TenantNameTableAdapter1.Fill(
        rentalEaseDataSet1.GetTenantReciept_TenantName)
).Start();

您可以使用相同的语法来调用 Control.Invoke,但稍微有些棘手,因为它可以接受 任何 委托 - 因此您需要告诉编译器您正在使用哪种类型,而不是依赖于隐式转换。可能最简单的方法是编写:

EventHandler eh = delegate
{
    // Code
};
control.Invoke(eh);
或者
EventHandler eh = (sender, args) =>
{
    // Code
};
control.Invoke(eh);
作为旁注,你们的名字真的那么长吗?你能缩短它们以获得更易读的代码吗?

我认为OP希望每个Fill()调用在自己的线程上运行。但除此之外,完全正确。 - LBushkin
@LBushkin:会进行调整。老实说,这不是一个非常清晰的问题。 - Jon Skeet
我本来想扩展我的答案,包括lambda,但你的答案更完整(而且更好)。BackgroundWorker组件仍然是一个选项,因为你不必担心Control.Invoke。 - RichardOD
好的,我会更新答案以保留 EventHandler 方面的内容,但方式会稍有不同。 - Jon Skeet
这些名称对应于存储过程或它们来自的表。我不知道为什么它们后面加了'1'。 - Malfist
显示剩余2条评论

30

与之前所说的类似 - 我发现任务会更简单(从 .net 4 开始支持,从 .net 4.5 开始可以按以下方式使用):

Task mytask = Task.Run(() => 
{
    //Lines of code
});

1
在 .net 4 中没有 Run 方法。 - Tommix
1
更正:任务在 .net 4 中变得可用,但 Task.Run 语法在 .net 4.5 中得到支持。 - JSideris

11

启动线程相对较为昂贵。

您可能最好使用线程池中的线程:

ThreadPool.QueueUserWorkItem(unused =>
    getTenantReciept_UnitTableAdapter1.Fill(
        rentalEaseDataSet1.GetTenantReciept_Unit)
);
ThreadPool.QueueUserWorkItem(unused =>
    getTenantReciept_TenantNameTableAdapter1.Fill(
        rentalEaseDataSet1.GetTenantReciept_TenantName)
);

你如何知道线程何时完成?例如:我这样做:ThreadPool.QueueUserWorkItem(unused => data[0] = get_data(a,b)); ThreadPool.QueueUserWorkItem(unused => data[1] = get_data(a2,b2)); - The Muffin Boy
1
现在是2016年。'ThreadPool.QueueUserWorkItem'已经过时了,可以使用Tasks(请参见@JSideris的答案)或'async'。两者都提供了等待任务完成并检索返回值的方法。 - oefe

8
您可以使用匿名方法:

void Foo()
{
    Thread myThread = new System.Threading.Thread(delegate(){
              //Your code here
     });
    myThread.Start();
}

1
它会出现错误:跨线程操作无效:从创建它的线程以外的线程访问控件。 - Prakash Vishwakarma
1
@PrakashVishwakarma,这是因为您正在访问表单的控件元素,但在与UI线程不同的线程上。您需要执行textboxInput.Invoke(new MethodInvoker(() => { Whatever(); }));,它将在UI线程上安排匿名函数() => { Whatever(); }。 - Adam White

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接