C# 多线程设计示例

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/596991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 09:42:06  来源:igfitidea点击:

C# Multi Thread Design Example

c#multithreadingdesign-patterns

提问by Brownman98

I am relatively new to C#/.Net. I'm developing a desktop application that requires multi threading. I came up with the following pattern below as a base. I was wondering if anyone could point out how to make it better in terms of coding, being thread safe, and being efficient.

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

Hopefully this makes some sense.

希望这是有道理的。



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.
    }
}

采纳答案by leppie

You don't need a BackgroundWorker unless you want to be spoonfed, normal threads are perfectly acceptable, as long as you follow the rules.

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

回答by muratgu

I don't see a good reason to create this abstraction over BackgroundWorker. If you insist, just a warning: I'm not sure if it changed in later releases, but in NET 2.0, it wasn't possible to really cancel the DoWork handler (unless it checked once in a while if it was asked to stop). Read herefor a solution.

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

回答by Jon Adams

Have you looked into the Microsoft Parallel Extensions to .NET Framework 3.5? It's a pretty good library that takes a lot of the work out of threading.

您是否查看过Microsoft Parallel Extensions to .NET Framework 3.5?这是一个相当不错的库,它消除了线程处理的大量工作。

There are also a lot of articles on MSDN about threading patterns that you should research too. Threading can get really complicated really fast. It's nice to have someone else to have though of all the important stuff that can go wrong, and simplify it down to a library or a pattern. Of course, there's danger in that too if you don't understand the gotchas of any particular solution. So, make sure you research well whatever solution you choose.

MSDN 上还有很多关于线程模式的文章,您也应该研究一下。线程可以很快变得非常复杂。很高兴让其他人处理所有可能出错的重要内容,并将其简化为库或模式。当然,如果您不了解任何特定解决方案的陷阱,那也存在危险。因此,请务必仔细研究您选择的任何解决方案。

回答by SwDevMan81

I have done something similar to this. There is a good reason if you do have multiple tasks that you want to perform, but you dont want to have BackgroundWorker code replicated through the entire project. I dont have the progressbar tied to the actual base class, I just have that in the main form. Here is the solution I came up with:

我做过类似的事情。如果您确实有多个要执行的任务,但又不想在整个项目中复制 BackgroundWorker 代码,这是有充分理由的。我没有将进度条绑定到实际的基类,我只是在主窗体中有它。这是我想出的解决方案:

The following is the base class:

以下是基类:


   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
   }

回答by SwDevMan81

The following is an example test class for the example I posted:

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


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

And here is what the main form would look like (very basic example):

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


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

回答by AndrewHymansonZA

I'm currently investigation Threadmare over at http://sklobovsky.nstemp.com/community/threadmare/threadmare.htmfor a C# project. It looks very, very useful. It's in Delphi, but the principles apply to any language that can handle events.

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