在c#中使用线程工作的最佳方法是什么?

时间:2009-11-22 15:29:26

标签: c# .net multithreading

在c#中使用线程工作(方法)的最佳方法是什么?

例如:

假设我有一个表单并希望从db加载数据。

My form controls: 
 - dataGridView (to show data from DB), 
 - label (loading status) and 
 - button (start loading).

当我点击按钮时,表单被冻结,直到任务完成。在任务完成之前,加载状态也不会改变。我认为异步线程会是答案吗?

所以我的问题是:处理这个问题的最佳方法是什么?我知道有很多关于线程的东西,但是它们之间的区别是什么?你如何使线程安全?

你如何解决这类问题?

最诚挚的问候。

4 个答案:

答案 0 :(得分:7)

如果使用Windows窗体,则应查看BackrgroundWorker。更一般地说,使用ThreadPool类通常很有用。最后,值得一看新的.NET 4的Parallel类。

答案 1 :(得分:3)

没有通用的'最佳'方式来处理工作。你只需要尝试不同的做事方式,我担心。

我特别喜欢杰里米·D·米勒(Jeremy D. Miller)描述的at this page的延续思想(向下滚动以找到“延续”部分)。它非常优雅,意味着编写很少的样板代码。

基本上,当您使用Func参数调用“ExecuteWithContinuation”时,该函数将异步执行,然后在完成时返回一个操作。然后将操作编组回您的UI线程以充当延续。这使您可以快速将操作拆分为两位:

  1. 执行不应阻止UI的长时间运行操作
  2. ...完成后,更新UI线程上的UI
  3. 需要一点时间来适应,但它很酷。

    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);
                                             });
        }
    }
    

    然后你就这样使用它(原谅格式......)

    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();
                                        };
                        });
    }
    

答案 2 :(得分:1)

您可以使用这种模式: -

    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);
        });
    }

答案 3 :(得分:0)

您无法从分离线程中运行的代码访问您的控件 - 框架不允许这样做,这解释了您获得的错误。

您需要在非表单对象中缓存从db检索到的数据,并在后台工作线程完成后使用该对象的数据填充UI(并处理同步以访问该对象)。