winforms - 具有启动、暂停和停止事件处理的动态线程

标签 winforms multithreading events c#-4.0 delegates

我已经创建了示例应用程序并实现了线程。基本上我想创建这个应用程序的目的是

  1. 如果有任何进程正在运行,则用户界面应通知 [完成]
  2. 使用 ProgressBar 处理动态创建的线程 [DONE]
  3. 为启动、暂停和停止线程提供附加功能 可用进度列表。 [需要您的帮助]

注意:-我对线程和委托(delegate)了解不多,所以请让我知道现有代码的最佳解决方案。

使用文件和控件:- 该演示应用程序基本上使用了三个文件

  • ProgressForm.cs(窗口表单) 其中包含用于创建新进度的按钮和用于保存所有创建的进度条的容器

  • ProgressClass.cs 其中包含动态线程和通知 UI 的委托(delegate),无需锁定或挂起用户界面

  • ProgressControl.cs(用户控件)
  • 其中包含
  • 进度条(显示已完成的进程)
  • Precent 标签(显示已完成进度的百分比)
  • 开始/暂停按钮(用于播放/暂停线程)
  • 停止按钮(停止正在运行的线程并从列表中删除进度)
  • StartTime标签(显示进程开始时间)
  • EndTime标签(显示进程完成的时间)
  • MaxValue Lable(生成 25 到 100 之间的随机数)
  • 代码片段:- 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

    最佳答案

    您将需要使用 ManualResetEventManualResetEventSlim 在线程中创建暂停和恢复行为。这个想法是在安全点检查工作线程中事件的状态。这是通过 WaitOneWait 方法完成的。如果事件发出信号,则调用将立即返回,允许线程继续进行。如果事件未发出信号,则调用将阻塞,直到通过 Set 方法发出事件信号。因此,要暂停线程,您可以调用 Reset 来取消事件信号,并恢复线程,您可以调用 Set

    只需记住在工作线程的指令序列中的安全点处调用 WaitOneWait 即可。换句话说,不要在锁或类似的东西内调用这些方法。在循环的开始或结束通常是一个好的开始。

    此外,您似乎使用 Invoke 方法来更新 UI。这一切都很好,但对于简单地使用进度信息更新 UI 有一个更好的选择。最好将进度信息发布到共享数据结构,然后让 UI 线程通过计时器获取它。我知道,对于那些关注我答案的人来说,我经常谈论这个问题。但是,这种策略有很多优点。

    • 它打破了 Invoke 所强加的 UI 和工作线程之间的紧密耦合。
    • 它将更新 UI 线程的责任放到了它应该所属的 UI 线程上。
    • UI 线程可以决定更新的时间和频率。
    • 不存在 UI 消息泵溢出的风险,而由工作线程启动的编码(marshal)技术会出现这种情况。
    • 工作线程不必等待确认已执行更新即可继续执行后续步骤(即,您可以在 UI 和工作线程上获得更多吞吐量)。
    • 它避免了尝试正常结束工作线程时可能发生的微妙竞争条件。
    • 它的效率更高,因为 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; }
    }
    

    关于winforms - 具有启动、暂停和停止事件处理的动态线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7362440/

    相关文章:

    c# - 如何使用 DateTimePicker 设置系统时间?

    ios - 有没有办法弄清楚 NSManagedObjectContext 在哪个线程上?

    excel - 如果字符计数器大于 100,则更改字体大小的事件

    javascript - function.call() 或 click()

    node.js - 为什么我不能在事件名称 Node js中输入数字?

    winforms - 为什么这个 WinForms 窗口在拖动时呈现伪像?

    c# - 有没有办法在 WinForm 表中有交替的行颜色

    c# - 拒绝基于对象数据的拖放?

    c - C 线程编程中未正确获取全局变量

    java - 如何通过多线程迭代concurrentLinkedQueue?