C#多线程设计实例

时间:2009-02-27 22:53:20

标签: c# multithreading design-patterns

我对C#/ .Net相对较新。我正在开发一个需要多线程的桌面应用程序。我想出了下面的以下模式作为基础。我想知道是否有人可以指出如何在编码,线程安全和高效方面做得更好。

希望这是有道理的。


public abstract class ThreadManagerBase
{
    // static class variables

    private static ThreadManagerBase instance = null;
    private static BackgroundWorker thread = null;
    private static ProgressBarUIForm progress = null;

    /// <summary>
    /// Create a new instance of this class. The internals are left to the derived class to figure out.
    /// Only one instance of this can run at any time. There should only be the main thread and this thread.
    /// </summary>
    public abstract static ThreadManagerBase NewInstance();

    /// <summary>
    /// Clears the instance.
    /// </summary>
    public static void ClearInstance()
    {
        instance = null;
    }

    /// <summary>
    /// Initializes the background worker with some presets.
    /// Displays progress bar.
    /// </summary>
    private abstract static void InitializeThread()
    {
        thread = new BackgroundWorker();

        thread.WorkerReportsProgress = true;
        thread.WorkerSupportsCancellation = true;

        thread.DoWork += new DoWorkEventHandler(thread_DoWork);
        thread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(thread_RunWorkerCompleted);
        thread.ProgressChanged += new ProgressChangedEventHandler(thread_ProgressChanged);

        thread.RunWorkerAsync();

        progress = new ProgressBarUIForm();

        progress.EnableCancelButton = true;

        progress.UserCanceled += new EventHandlerCancelClicked(progress_UserCanceled);

        progress.ShowDialog();

        thread.Dispose();

        thread = null;
    }

    private static void progress_UserCanceled(bool userCanceled)
    {
        thread.CancelAsync();
    }

    private static void thread_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progress.SetProgressLevel = e.ProgressPercentage;
        progress.SetProgressMessage = e.UserState.ToString();
    }

    private static void thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        progress.Close();

        progress = null;
    }

    private static void thread_DoWork(object sender, DoWorkEventArgs e)
    {
        ProcessWork();
    }

    private abstract static void ProcessWork()
    {
        // do actuall stuff here.
        // the derived classes will take care of the plumbing.
    }
}

6 个答案:

答案 0 :(得分:1)

你有没有看过Microsoft Parallel Extensions to .NET Framework 3.5?这是一个非常好的库,需要大量的线程工作。

在MSDN上也有很多关于线程模式的文章,你也应该研究。线程可以非常快速地变得非常复杂。让别人拥有可能出错的所有重要内容并将其简化为库或模式是很好的。当然,如果你不理解任何特定解决方案的问题,也存在危险。因此,请确保您研究出您选择的任何解决方案。

答案 1 :(得分:0)

我没有看到在BackgroundWorker上创建此抽象的充分理由。 如果你坚持,只是一个警告:我不确定它是否在以后的版本中发生了变化,但是在.NET 2.0中,无法真正取消DoWork处理程序(除非它被要求停止一段时间检查一次) )。请阅读here以获取解决方案。

答案 2 :(得分:0)

我做了类似的事情。如果您确实要执行多个任务,那么有一个很好的理由,但您不希望在整个项目中复制BackgroundWorker代码。我没有将progressbar绑定到实际的基类,我只是在主窗体中。这是我提出的解决方案:

以下是基类:


   public abstract class Operation
   {
      #region public Event Handlers

      /// 
      /// The event that updates the progress of the operation
      /// 
      public event OperationProgressChangedEventHandler OperationProgressChanged;

      /// 
      /// The event that notifies that the operation is complete (and results)
      /// 
      public event OperationCompletedEventHandler OperationCompleted;

      #endregion

      #region Members

      // Whether or not we can cancel the operation
      private bool mWorkerSupportsCancellation = false;
      // The task worker that handles running the operation
      private BackgroundWorker mOperationWorker;
      // The operation parameters
      private object[] mOperationParameters;

      #endregion

      /// 
      /// Base class for all operations
      /// 
      public Operation(params object[] workerParameters)
      {
         mOperationParameters = workerParameters;
         // Setup the worker
         SetupOperationWorker();
      }

      #region Setup Functions

      /// 
      /// Setup the background worker to run our Operations
      /// 
      private void SetupOperationWorker()
      {
         mOperationWorker = new BackgroundWorker();
         mOperationWorker.WorkerSupportsCancellation = mWorkerSupportsCancellation;
         mOperationWorker.WorkerReportsProgress = true;
         mOperationWorker.WorkerSupportsCancellation = true;
         mOperationWorker.DoWork += new DoWorkEventHandler(OperationWorkerDoWork);
         mOperationWorker.ProgressChanged += new ProgressChangedEventHandler(OperationWorkerProgressChanged);
         mOperationWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(OperationWorkerRunWorkerCompleted);
      }

      #endregion

      #region Properties

      /// 
      /// Whether or not to allow the user to cancel the operation
      /// 
      public bool CanCancel
      {
         set
         {
            mWorkerSupportsCancellation = value;
         }
      }

      #endregion

      #region Operation Start/Stop Details

      /// 
      /// Start the operation with the given parameters
      /// 
      /// The parameters for the worker
      public void StartOperation()
      {
         // Run the worker
         mOperationWorker.RunWorkerAsync(mOperationParameters);
      }

      /// 
      /// Stop the operation
      /// 
      public void StopOperation()
      {
         // Signal the cancel first, then call cancel to stop the test
         if (IsRunning())
         {
            // Sets the backgroundworker CancelPending to true, so we can break
            // in the sub classes operation
            mOperationWorker.CancelAsync();
            // This allows us to trigger an event or "Set" if WaitOne'ing
            Cancel();
            // Wait for it to actually stop before returning
            while (IsRunning())
            {
               Application.DoEvents();
            }
         }
      }

      /// 
      /// Whether or not the operation is currently running
      /// 
      /// 
      public bool IsRunning()
      {
         return mOperationWorker.IsBusy;
      }

      #endregion

      #region BackgroundWorker Events

      /// 
      /// Fires when the operation has completed
      /// 
      /// 
      /// 
      private void OperationWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
      {
         // Allow the sub class to clean up anything that might need to be updated
         Clean();

         // Notify whoever is register that the operation is complete
         if (OperationCompleted != null)
         {
            OperationCompleted(e);
         }
      }

      ///  
      /// Fires when the progress needs to be updated for a given test (we might not care)
      /// 
      /// 
      /// 
      private void OperationWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
      {
         // Notify whoever is register of what the current percentage is
         if (OperationProgressChanged != null)
         {
            OperationProgressChanged(e);
         }
      }

      /// 
      /// Fires when we start the operation (this does the work)
      /// 
      /// 
      /// 
      private void OperationWorkerDoWork(object sender, DoWorkEventArgs e)
      {
         // Run the operation
         Run(sender, e);
      }

      #endregion

      #region Abstract methods

      /// 
      /// Abstract, implemented in the sub class to do the work
      /// 
      /// 
      /// 
      protected abstract void Run(object sender, DoWorkEventArgs e);

      /// 
      /// Called at the end of the test to clean up anything (ex: Disconnected events, etc)
      /// 
      protected abstract void Clean();

      /// 
      /// If we are waiting on something in the operation, this will allow us to
      /// stop waiting (ex: WaitOne).
      /// 
      protected abstract void Cancel();

      #endregion
   }

答案 3 :(得分:0)

以下是我发布的示例的示例测试类:


   class TestOperation : Operation
   {
      AutoResetEvent mMsgRec;

      public TestOperation(params object[] workerParameters)
         : base(workerParameters)
      {
         CanCancel = true;
         mMsgRec = new AutoResetEvent(false);
         //mSomeEvent += DoSomething();
      }

      protected override void Cancel()
      {
         mMsgRec.Set();
      }

      protected override void Clean()
      {
         //mSomeEvent -= DoSomething();
      }

      protected override void Run(object sender, DoWorkEventArgs e)
      {
         BackgroundWorker bg = (BackgroundWorker)sender;
         for (int i = 0; !bg.CancellationPending && (i < 90); i++)
         {
            bg.ReportProgress(i);
            Thread.Sleep(100);
         }

         for (int i = 90; !bg.CancellationPending && (i < 100); i++)
         {
            mMsgRec.WaitOne(2000, false);
            bg.ReportProgress(i);
         }

         if (bg.CancellationPending)
         {
            e.Cancel = true;
         }
         else
         {
            e.Result = "Complete"; // Or desired result
         }
      }
   }

这是主要形式的样子(非常基本的例子):


   public partial class Form1 : Form
   {
      TestOperation t;

      public Form1()
      {
         InitializeComponent();
      }

      private void button1_Click(object sender, EventArgs e)
      {
         t = new TestOperation();
         t.CanCancel = true;

         t.OperationProgressChanged += new OperationProgressChangedEventHandler(t_OperationProgressChanged);
         t.OperationCompleted += new OperationCompletedEventHandler(t_OperationCompleted);

         t.StartOperation();
      }

      void t_OperationCompleted(RunWorkerCompletedEventArgs e)
      {
         progressBar1.Value = 0;
      }

      void t_OperationProgressChanged(ProgressChangedEventArgs e)
      {
         progressBar1.Value = e.ProgressPercentage;
      }

      private void button2_Click(object sender, EventArgs e)
      {
         t.StopOperation();
      }
   }

答案 4 :(得分:0)

我目前正在http://sklobovsky.nstemp.com/community/threadmare/threadmare.htm调查Threadmare的C#项目。它看起来非常非常有用。它在Delphi中,但这些原则适用于任何可以处理事件的语言。

答案 5 :(得分:-2)

你不需要BackgroundWorker,除非你想要用勺子,普通线程是完全可以接受的,只要你遵守规则。