C # Multi Thread Design Пример - PullRequest
       10

C # Multi Thread Design Пример

2 голосов
/ 28 февраля 2009

Я относительно новичок в 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 ]

1 голос
/ 06 марта 2009

Рассматривали ли вы Microsoft Parallel Extensions для .NET Framework 3.5 ? Это довольно хорошая библиотека, которая берет на себя большую часть работы с потоками.

На MSDN также есть много статей о шаблонах потоков, которые вы также должны исследовать. Потоки могут стать действительно сложными очень быстро. Приятно иметь кого-то еще, чтобы иметь в виду все важные вещи, которые могут пойти не так, и упростить его до библиотеки или шаблона. Конечно, в этом тоже есть опасность, если вы не понимаете хитростей какого-либо конкретного решения. Поэтому убедитесь, что вы тщательно исследуете любое решение, которое выберете.

0 голосов
/ 07 декабря 2009

В настоящее время я изучаю Threadmare на http://sklobovsky.nstemp.com/community/threadmare/threadmare.htm для проекта на C #. Это выглядит очень, очень полезно. Это в Delphi, но принципы применимы к любому языку, который может обрабатывать события.

0 голосов
/ 05 июня 2009

Ниже приведен пример тестового класса для примера, который я разместил:


   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 &lt 90); i++)
         {
            bg.ReportProgress(i);
            Thread.Sleep(100);
         }

         for (int i = 90; !bg.CancellationPending && (i &lt 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();
      }
   }

0 голосов
/ 05 июня 2009

Я сделал что-то похожее на это. Есть веская причина, если у вас есть несколько задач, которые вы хотите выполнить, но вы не хотите, чтобы код BackgroundWorker реплицировался по всему проекту. У меня нет индикатора выполнения, привязанного к реальному базовому классу, у меня это просто в основной форме. Вот решение, которое я придумал:

Ниже приведен базовый класс:


   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
   }

0 голосов
/ 28 февраля 2009

Я не вижу веской причины для создания этой абстракции поверх BackgroundWorker. Если вы настаиваете, просто предупреждение: я не уверен, изменилось ли оно в более поздних выпусках, но в NET 2.0 было невозможно реально отменить обработчик DoWork (если только он не проверял время от времени, если его попросили остановить ). Прочитайте здесь для решения.

0 голосов
/ 28 февраля 2009

Вам не нужен BackgroundWorker, если вы не хотите использовать ложку, нормальные темы вполне приемлемы, если вы следуете правилам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...