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

发布于 2024-12-03 18:57:59 字数 6067 浏览 0 评论 0原文

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

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

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

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

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

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

  3. ProgressControl.cs(用户控件)
  4. 其中包含
  5. 进度条(显示已完成的流程)
  6. Precent 标签(显示已完成进度的百分比)
  7. 开始/暂停按钮(用于播放/暂停线程)
  8. 停止按钮(停止正在运行的线程并从列表中删除进度)
  9. StartTime 标签(显示进程开始时间)
  10. EndTime 标签(显示进程完成时间)
  11. MaxValue 标签(生成 25 到 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.
        }

更新:

在此处输入图像描述

I have created sample application and implemented threading. basically aim to craete this application is i would like to

  1. If any process(s) are runnig then User Interface should Notify
    [DONE]
  2. Handle dynamically created thread with ProgressBar [DONE]
  3. Provide addition functionality to Start, Pause and Stop thread from
    available progress list. [NEED YOUR HELP]

Note:- I don't have much knowledge about Threading and Delegates, so please let me know best solution for existing code.

Files and Controls are used:-
Basically three files are used in this demo application

  1. ProgressForm.cs (Window Form)
    which conatains Button for creating new progress and Container whic will hold all the created progressbars

  2. ProgressClass.cs
    Which contains Dynamic Threading and Delegates to Notify UI without locking or hanging user interface

  3. ProgressControl.cs (User Control)
  4. Which contains
  5. Progressbar (to display process done)
  6. Precent Label (display percentage of completed progress)
  7. Start/Pause button (for play/pause a thread)
  8. Stop button (stop running thread and remove progress from list)
  9. StartTime Label (display process started time)
  10. EndTime label (display time of process completed)
  11. MaxValue Lable (generate random number between 25 to 100)

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

I am not asking for write whole code instead of provide hint.

I would like to start/pause relevent thread from list, os what should i do for that?
I would like hind in following function:

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

UPDATED:

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

久而酒知 2024-12-10 18:57:59

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

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

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

  • 它打破了 Invoke 强加的 UI 和工作线程之间的紧密耦合。
  • 它将更新 UI 线程的责任放在了无论如何都应该属于的 UI 线程上。
  • UI 线程可以决定更新的时间和频率。
  • 不存在 UI 消息泵溢出的风险,就像工作线程启动的封送技术的情况一样。
  • 工作线程在继续下一步之前不必等待更新已执行的确认(即,您在 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; }
}

You will want to use a ManualResetEvent or ManualResetEventSlim to create the pause and resume behavior in the thread. The idea is to check the state of the event in the worker thread at safe points. This is done via the WaitOne or Wait methods. If the event is signaled then the calls will return immediately allowing the thread to proceed. If the event is unsignaled then the calls block until the event is signaled via the Set method. So to pause the thread you would call Reset to unsignal the event and to resume the thread you would call Set.

Just remember to place calls to WaitOne or Wait at safe points in the instruction sequence of the worker thread. In other words, do not call these methods inside a lock or something like that. At the beginning or end of a loop is often a good start.

Also, it looks like you use the Invoke method for updating the UI. That is all fine and good, but for simply updating the UI with progress information there is a better option. It is better to publish the progress information to a shared data structure and then have the UI thread pick it up via a timer. For those that monitor my answers I harp about this a lot, I know. But, this strategy has a lot of advantages.

  • It breaks the tight coupling between the UI and worker threads that Invoke imposes.
  • It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway.
  • The UI thread gets to dictate when and how often the update should take place.
  • There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker thread.
  • The worker thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker threads).
  • It avoids the subtle race conditions that can occur when trying to gracefully end the worker thread.
  • It is more efficient since Invoke is an expensive operation.

Update:

Here is the general idea regarding the changes that could be made to 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; }
}
烈酒灼喉 2024-12-10 18:57:59

通常认为挂起线程是不好的做法(尽管它是可能的) 。暂停和终止线程的正确方法是通过与线程正在执行的作业配合。该作业应检查循环中的变量,并相应地暂停或退出。控制程序可以设置该变量,如果需要反馈,后台线程可以在退出或休眠之前调用通知方法。

It is generally considered bad practice to Suspend threads (though it is possible). The right way to pause and terminate threads is through the cooperation with the job that the thread is doing. The job should check a variable in a loop, and pause or exit accordingly. The controlling program can set that variable, and if you need feedback the background thread can call a notification method before exiting or sleeping.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文