在c#中线程工作的最佳方式是什么?
在c#中执行工作(方法)的最佳方式是什么 例如: 假设我有一个表单,希望从db加载数据在c#中线程工作的最佳方式是什么?,c#,.net,multithreading,C#,.net,Multithreading,在c#中执行工作(方法)的最佳方式是什么 例如: 假设我有一个表单,希望从db加载数据 My form controls: - dataGridView (to show data from DB), - label (loading status) and - button (start loading). 当我点击按钮时,我的表单将被冻结,直到任务完成。此外,在任务完成之前,加载状态不会更改。我认为异步线程将是答案 所以我的问题是:处理这个问题的最好方法是什么?我知道有很多关于
My form controls:
- dataGridView (to show data from DB),
- label (loading status) and
- button (start loading).
当我点击按钮时,我的表单将被冻结,直到任务完成。此外,在任务完成之前,加载状态不会更改。我认为异步线程将是答案
所以我的问题是:处理这个问题的最好方法是什么?我知道有很多关于线程的东西,但是它们之间的区别是什么?如何使线程安全
你如何解决这类问题
致以最诚挚的问候。如果使用Windows窗体,您应该查看。更一般地说,使用该类通常是有用的。最后,值得一看新的.NET 4类。您可以使用这种模式:-
private void RefreshButton_Click(object sender, EventArgs e)
{
MessageLabel.Text = "Working...";
RefreshButton.Enabled = false;
ThreadPool.QueueUserWorkItem(delegate(object state)
{
// do work here
// e.g.
object datasource = GetData();
this.Invoke((Action<object>)delegate(object obj)
{
// gridview should also be accessed in UI thread
// e.g.
MyGridView.DataSource = obj;
MessageLabel.Text = "Done.";
RefreshButton.Enabled = true;
}, datasource);
});
}
private void刷新按钮\u单击(对象发送者,事件参数e)
{
MessageLabel.Text=“正在工作…”;
RefreshButton.Enabled=false;
ThreadPool.QueueUserWorkItem(委托(对象状态)
{
//你在这里工作吗
//例如。
对象数据源=GetData();
调用((操作)委托(对象obj)
{
//gridview也应该在UI线程中访问
//例如。
MyGridView.DataSource=obj;
MessageLabel.Text=“完成。”;
RefreshButton.Enabled=true;
},数据源);
});
}
没有通用的“最佳”线程工作方式。恐怕你得尝试不同的做事方式
我特别喜欢Jeremy D.Miller所描述的延续思想(向下滚动找到“延续”部分)。它非常优雅,意味着只需编写很少的样板代码
基本上,当您使用Func参数调用“ExecuteWithContinuation”时,函数将异步执行,然后在完成时返回操作。然后,该操作被封送回UI线程以作为继续。这允许您将操作快速拆分为两位:
public class AsyncCommandExecutor : ICommandExecutor
{
private readonly SynchronizationContext m_context;
public AsyncCommandExecutor(SynchronizationContext context)
{
if (context == null) throw new ArgumentNullException("context");
m_context = context;
}
public void Execute(Action command)
{
ThreadPool.QueueUserWorkItem(o => command());
}
public void ExecuteWithContinuation(Func<Action> command)
{
ThreadPool.QueueUserWorkItem(o =>
{
var continuation = command();
m_context.Send(x => continuation(), null);
});
}
}
您无法通过在派生线程中运行的代码访问控件-框架不允许这样做,这就解释了您所遇到的错误
您需要将从db检索到的数据缓存在一个非表单对象中,并在后台工作线程完成后用该对象的数据填充UI(并处理访问该对象的同步)。谢谢您的回答。在何处调用Control.Invoke()?我收到一个错误:跨线程操作无效:控件“dataGridView”是从创建它的线程以外的线程访问的。我已将代码转换为Winforms。请注意,您还必须在UI线程中操作gridview,因为它是一个UI元素。这将消除您看到的运行时异常。我已将框架更改为3.5。现在它起作用了。谢谢你,亚当!谢谢你的回答。是的,我正在使用win窗体和.NET2.0。我尝试了AdamRalph的答案和get中的ThreadPool,错误:跨线程操作无效:控件“dataGridView”是从创建它的线程以外的线程访问的。一旦将工作移动到另一个线程上,它就不能再直接访问用户界面(因此跨线程调用)。您需要使用Control.Invoke()或Control.BeginInvoke()将控件传递回UI线程,要求它为您更新UI。这就是为什么
BackgroundWorker
类很好;它会在UI线程上引发事件,因此不需要Invoke()
。谢谢您的回答。如果没有.NET2.0中的lambda,我如何使用它?Mark,现在我在.NET3.5上。我遇到错误:使用泛型类型“System.Action”需要“1”类型参数。做什么?“操作”是具有一个参数的通用操作;我发布的代码只使用了没有参数的“Action”。看起来你想用行动代替行动。嘿,马克。我试过你的解决方案,效果很好。您能演示一下如何从ExecuteWithContinuation部分返回一些值吗?假设我有一些需要在完成时更新的东西(inti),并且想要更改变量。怎么做?在c#中线程工作的最佳方式是什么?小心实际上,CrossThreadException
只会在调试模式下抛出。
public void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished()
{
DisableUi();
m_commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
ConnectToServer();
// This is the continuation that will be run
// on the UI thread
return () =>
{
EnableUi();
};
});
}