Динамическая потоковая обработка с обработкой событий Start, Pause и Stop - PullRequest
5 голосов
/ 09 сентября 2011

Я создал пример приложения и реализовал многопоточность. в основном стремиться создать это приложение, я хотел бы

  1. Если какой-либо процесс (ы) запущен, пользовательский интерфейс должен уведомить [ DONE ]
  2. Обработка динамически созданного потока с помощью ProgressBar [ DONE ]
  3. Предоставляет дополнительные функции для запуска, приостановки и остановки потока с доступный список прогресса. [ НУЖНА ВАША ПОМОЩЬ ]

Примечание: - У меня мало знаний о потоках и делегатах, поэтому, пожалуйста, дайте мне знать лучшее решение для существующего кода.

Используются файлы и элементы управления: - В основном, в этом демонстрационном приложении используются три файла

  1. ProgressForm.cs (форма окна) которая содержит кнопку для создания нового прогресса и контейнер, который будет содержать все созданные индикаторы выполнения

  2. ProgressClass.cs Содержит динамическую многопоточность и делегаты для уведомления интерфейса без блокировки или зависания пользовательского интерфейса

  3. ProgressControl.cs (контроль пользователя)
  4. Который содержит
  5. Прогрессбар (для отображения выполненного процесса)
  6. Precent Label (отображать процент завершенного прогресса)
  7. Кнопка Пуск / Пауза (для воспроизведения / паузы в потоке)
  8. Кнопка «Стоп» (остановка запуска потока и удаление прогресса из списка)
  9. StartTime Label (отображение времени начала процесса)
  10. Метка EndTime (отображение времени завершения процесса)
  11. MaxValue Lable (генерирует случайное число от 25 до 100)

КОД SNIPPET: - 1. ProgressForm .cs

public partial class ProgressForm : Form
    {
        Random randomMaxValue = new Random();
        public ProgressForm()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
             ProgressClass m_clsProcess;
             ProgressControl progress = new ProgressControl();
             progress.StartedAt = DateTime.Now;
             progress.MinValue = 0;
             progress.CurrentValue = 0;
             progress.MaxValue = randomMaxValue.Next(25, 100);
             AddControl(progress);
             m_clsProcess = new ProgressClass(progress, this, new ProgressClass.NotifyProgress(DelegateProgress));
             m_clsProcess.Start();
        }
        private void DelegateProgress(int CurrentValue, ProgressControl Progress)
        {
            ProgressBar p = (ProgressBar)Progress.Controls.Find("pgbPercent", false)[0];
            p.Minimum = Progress.MinValue;
            p.Value = CurrentValue;
            p.Maximum = Progress.MaxValue;

            Label percent = (Label)Progress.Controls.Find("lblPercent", false)[0];
            percent.Text = string.Format("{0:#00} %", Convert.ToInt16((CurrentValue * 100) / Progress.MaxValue));

            Label start = (Label)Progress.Controls.Find("lblStart", false)[0];
            start.Text = string.Format("{0:HH:mm:ss}", Progress.StartedAt);

            if (CurrentValue == Progress.MaxValue)
            {
                Label complete = (Label)Progress.Controls.Find("lblComplete", false)[0];
                complete.Text = string.Format("{0:HH:mm:ss}", DateTime.Now);
                Progress.Status = ProgressControl.ProgressStatus.Completed;
            }

            Label max = (Label)Progress.Controls.Find("lblMaxValue", false)[0];
            max.Text = string.Format("{0:#00}", Progress.MaxValue);

            Button btnstartstop = (Button)Progress.Controls.Find("btnStartStop", false)[0];
            btnstartstop.Click += new EventHandler(ProgressStartStop);
        }
        private void AddControl(Control ctl)
        {
            tableLayoutPnl.RowCount += 1;
            tableLayoutPnl.RowStyles.Add(new RowStyle());
            ctl.Dock = DockStyle.Fill;
            tableLayoutPnl.Controls.Add(ctl, 0, tableLayoutPnl.RowCount - 1);
        }
        void ProgressStartStop(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            //
            //Here i would like to write a code for START / PAUSE thread and update Image acording too.
            //
        }
    }

2. ProgressControl.cs

public partial class ProgressControl : UserControl
    {
        public enum ProgressStatus
        {
            Initialize,
            Running,
            Paused,
            Completed
        }

        public DateTime StartedAt { get; set; }
        public DateTime CompletedAt { get; set; }
        public int MinValue { get; set; }
        public int CurrentValue { get; set; }
        public int MaxValue { get; set; }
        public ProgressStatus Status { get; set; }

        public ProgressControl()
        {
            InitializeComponent();
            this.Status = ProgressStatus.Initialize;
        }
    }

3. ProgressClass.cs

public class ProgressClass
{
    private int ThreadWaitTime = 100;
    private ProgressControl m_progress;
    private NotifyProgress m_clsNotifyDelegate;
    private System.Threading.Thread m_clsThread;

    private System.ComponentModel.ISynchronizeInvoke m_clsSynchronizingObject;
    public delegate void NotifyProgress(int PercentComplete, ProgressControl Progress);

    public ProgressClass(ProgressControl Progress, System.ComponentModel.ISynchronizeInvoke SynchronizingObject, NotifyProgress NotifyDelegate)
    {
        m_progress = Progress;
        m_clsSynchronizingObject = SynchronizingObject;
        m_clsNotifyDelegate = NotifyDelegate;
    }

    public void Start()
    {
        m_clsThread = new System.Threading.Thread(DoProcess);
        m_clsThread.Name = "Background Thread";
        m_clsThread.IsBackground = true;
        m_progress.Status = ProgressControl.ProgressStatus.Running;
        m_clsThread.Start();
    }
    private void DoProcess()
    {
        for (int i = m_progress.MinValue; i <= m_progress.MaxValue; i++)
        {
            NotifyUI(i);
            Thread.Sleep(ThreadWaitTime);
        }
    }
    private void NotifyUI(int Value)
    {
        object[] args = new object[2];
        args[0] = Value;
        args[1] = m_progress;
        m_clsSynchronizingObject.Invoke(m_clsNotifyDelegate, args);
    }
}

Я не прошу писать весь код вместо подсказки.

Я хотел бы запустить / приостановить соответствующую ветку из списка, что мне делать для этого? Я хотел бы указать в следующей функции:

   void ProgressStartStop(object sender, EventArgs e)
        {
            Button btn = sender as Button;
            //Here i would like to write a code for START / PAUSE thread and update Image acording too.
        }

ОБНОВЛЕНИЕ:

enter image description here

Ответы [ 2 ]

3 голосов
/ 09 сентября 2011

Вы захотите использовать ManualResetEvent или ManualResetEventSlim, чтобы создать поведение паузы и возобновления в потоке. Идея состоит в том, чтобы проверить состояние события в рабочем потоке в безопасных точках. Это делается с помощью методов WaitOne или Wait. Если событие сигнализируется, то вызовы немедленно возвращаются, позволяя потоку продолжить. Если событие не сигнализируется, то вызовы блокируются, пока событие не будет сигнализировано с помощью метода Set. Поэтому, чтобы приостановить поток, вы должны вызвать Reset, чтобы отменить событие и возобновить поток, который вы бы назвали Set.

Только не забудьте сделать вызовы к WaitOne или Wait в безопасных точках в последовательности инструкций рабочего потока. Другими словами, не вызывайте эти методы внутри lock или что-то в этом роде. В начале или конце цикла часто хорошее начало.

Кроме того, похоже, что вы используете метод Invoke для обновления пользовательского интерфейса. Это все хорошо, но для простого обновления пользовательского интерфейса информацией о прогрессе есть лучший вариант. Лучше опубликовать информацию о ходе выполнения в общей структуре данных, а затем заставить поток пользовательского интерфейса забирать ее через таймер. Я знаю, что для тех, кто следит за моими ответами, я много об этом говорю. Но эта стратегия имеет много преимуществ.

  • Это нарушает жесткую связь между пользовательским интерфейсом и рабочими потоками, которые навязывает Invoke.
  • Он возлагает ответственность за обновление потока пользовательского интерфейса на поток пользовательского интерфейса, к которому он все равно должен принадлежать.
  • Поток пользовательского интерфейса определяет, когда и как часто должно происходить обновление.
  • Нет риска переполнения насоса сообщений пользовательского интерфейса, как в случае с методами маршалинга, инициированными рабочим потоком.
  • Рабочий поток не должен ждать подтверждения того, что обновление было выполнено, прежде чем перейти к следующим шагам (т. Е. Вы получаете большую пропускную способность как для пользовательского интерфейса, так и для рабочих потоков).
  • Это позволяет избежать тонких условий гонки, которые могут возникнуть при попытке изящно завершить рабочий поток.
  • Это более эффективно, поскольку Invoke - дорогостоящая операция.

Обновление:

Вот общая идея относительно изменений, которые могут быть внесены в ProgressStartStop.

private Dictionary<int, ThreadInfo> threads = new Dictionary<int, ThreadInfo>();

void ProgressStartStop(object sender, EventArgs e)
{
  Button button = sender as Button;
  int index = GetThreadIndexFromButton(button);
  if (!threads.ContainsKey(index))
  {
    // The thread has not been started yet so do it now.
    var thread = new Thread(RunThread);
    thread.Start();
    var mres = new ManualResetEventSlim(true);
    var info = new ThreadInfo { Thread = thread, ProceedSignal = mres };
    threads.Add(index, info);
    // Change the button image here.
  }
  else
  {
    ThreadInfo info = threads[index];
    if (info.ProceedSignal.Wait(0))
    {
      // The event is signaled which means the thread is running. Pause it.
      info.ProceedSignal.Reset();
      // Change the button image here.
    }
    else
    {
      // The event is unsignaled which means the thread is paused. Resume it.
      info.ProceedSignal.Set();
      // Change the button image here.
    }
  }
}

private class ThreadInfo
{
  Thread Thread { get; set; }
  ManualResetEventSlim ProceedSignal { get; set; }
}
0 голосов
/ 09 сентября 2011

Обычно приостановка потоков считается плохой практикой (хотя возможна ).Правильный способ приостановить и завершить потоки - это сотрудничество с работой, которую выполняет поток.Задание должно проверять переменную в цикле и соответственно останавливаться или выходить.Управляющая программа может установить эту переменную, и, если вам нужна обратная связь, фоновый поток может вызвать метод уведомления перед выходом или сном.

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