What is the best way to thread work in c#?
What's the best way to thread work (methods) in c#?
For example:
Let's say I have a form and want to load data from db.
My form controls:
- dataGridView (to show data from DB),
- label (loading status) and
- button (start loading).
When I click the button my form is frozen until the task is done. Also the loading status does not change until task is done. I think async threading would be the answ开发者_JAVA技巧er?
So my question: what's the best way to handle this? I know there is a lot stuff about Threading, but what's the difference between them and how do you make it thread safe?
How do you solve this kind of problems?
Best Regards.
If using Windows Forms, you should look at BackrgroundWorker. More generally, it is often useful to use the ThreadPool class. And finally, it is worth to take a look at the new .NET 4's Parallel class.
There is no universal 'best' way to thread work. You just have to try different ways of doing things, I'm afraid.
I particularly like Jeremy D. Miller's continuation idea described at this page (scroll down to find the "continuations" section). It's really elegant and means writing very little boilerplate code.
Basically, when you call "ExecuteWithContinuation" with a Func argument, the function is executed asynchronously, then returns an action when it finishes. The action is then marshalled back onto your UI thread to act as a continuation. This allows you to quickly split your operations into two bits:
- Perform long running operation that shouldn't block the UI
- ... when finished, update the UI on the UI thread
It takes a bit of getting used to, but it's pretty cool.
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);
});
}
}
You'd then use it like this (forgive the formatting...)
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();
};
});
}
You can use this kind of pattern:-
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);
});
}
You cannot access your controls from the code that runs in the spun-off thread - the framework does not allow this, which explains the error you are getting.
You need to cache the data retrieved from the db in a non-forms object and populate your UI with data from that object after the background worker thread is done (and handle synchronization for access to that object).
精彩评论