0

我在 C# 4.0 中实现了一个漫画阅读器,由于我实现了一些过程,从一个图像浏览到下一个图像需要一些时间。

因此,我以这样一种方式实现它,即 UI 线程将首先显示未处理的图像,而后台线程正在处理图像,然后替换未处理的图像。

一切正常,但现在,一些用户会疯狂地喜欢连续点击下一张图片,这会导致后台工作人员处理所有这些点击并显示所有图片。

我想要什么:如果用户点击多次,我希望后台工作人员只处理最后一个线程。

我做了什么:现在,我实现了一个检查活动线程数的函数,如果活动线程大于 1,后台线程将不会处理但返回之前的图像(这不好,因为未处理的图像会领先一个指数)

如果您有想法,请像初学者一样向我解释!

private void button4_Click(object sender, EventArgs e)
{

    Bitmap b = new Bitmap(this.CurrImage);

           if (!shutdown)
          {
            process_updateThread = new Thread(new ThreadStart(process_update));
            process_updateThread.Start();
          }

    pictureBox1.Image = b; //image will be replaced by worker thread image 
    pictureBox1.Location = ImageEdit.CalculateLocationImage(b);
    SetBackColor(b);
    ShowPageCount();
    updateNavButtons(); 
}


void StopThread()
{
    if(((IEnumerable)System.Diagnostics.Process.GetCurrentProcess().Threads).OfType<System.Diagnostics.ProcessThread>()
        .Where(t => t.ThreadState == System.Diagnostics.ThreadState.Running).Count() > 1) 
    shutdown = true;
    else shutdown = false;
}
4

2 回答 2

0

我假设您长期运行的进程是 process_update。

在运行下一个之前,您必须停止所有正在运行的 process_updates。但不要使用布尔变量来做到这一点!!!您必须使用同步对象。很可能它应该是 ManualResetEvent。

更新:

这个非常简单的例子可以让你了解多线程和线程管理

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;


namespace WindowsFormsExamples
{
    public partial class OnlyOneThread : Form
    {
        List<ManualResetEvent> threadStopEvents;    //This will hold stop events for running threads

        public OnlyOneThread()
        {
            InitializeComponent();
            threadStopEvents = new List<ManualResetEvent>();
        }

        private void runThreadBtn_Click(object sender, EventArgs e)
        {
            ManualResetEvent evt = new ManualResetEvent(false);
            ParameterizedThreadStart ts = new ParameterizedThreadStart(this.ThreadFunc);
            Thread t = new Thread(ts);
            t.Start(evt);
        }

        private delegate void UptadeThreadCountDelegate();   //This delegate is used by Invoke method
        private void UpdateThreadCount()
        {
            threadcountLbl.Text = threadStopEvents.Count.ToString();
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            //We must stop threads if they are still running

            lock (threadStopEvents)  // locking prevents simultaneous list access
            {
                foreach (ManualResetEvent evt in threadStopEvents)
                {
                    evt.Set(); //signal all events
                }
            }
        }


        //This is thread function
        private void ThreadFunc(Object obj)
        {
            ManualResetEvent stopEvent = obj as ManualResetEvent; //cast an object that was passed by Thread.Start()

            lock (threadStopEvents) // locking prevents simultaneous list access
            {
                foreach (ManualResetEvent evt in threadStopEvents)
                {
                    evt.Set(); //signal all events for all other threads to stop
                }

                threadStopEvents.Add(stopEvent);  //Put our event on list
            }

            if (this.IsHandleCreated) // This is necessary for invocation
                this.Invoke(new UptadeThreadCountDelegate(this.UpdateThreadCount));  //Invoke counter update

            for (int i = 0; i < 60; i++) // this will run about 1 minute
            {
                if (stopEvent.WaitOne(0)) // Tests stopEvent and continues
                {
                    //Stop signaled!!! exit!
                    break;
                }

                Thread.Sleep(1000); //Sleep 1 second
            }
            lock (threadStopEvents) // locking prevents simultaneous list access
            {
                threadStopEvents.Remove(stopEvent); //remove stop event from list
            }
            if (this.IsHandleCreated) // This is necessary for invocation
                this.Invoke(new UptadeThreadCountDelegate(this.UpdateThreadCount));  //Invoke counter update
        }
    }
}

如果要运行此示例,您必须创建 WindowsForms 项目并在窗体上添加 Button 和标签,然后使用此代码绑定到这些控件。注意表单方法的调用。当从非 GUI 线程执行时,这是必要的。

于 2013-02-21T07:16:43.990 回答
0

我看不到这个问题的简单解决方案……多线程从来都不是一件容易的事。就个人而言,我会提出以下建议(生产者/消费者情况的一种偏差):

  • 首先有一个通用计数器,它表示当前要渲染的图像(可以是一个简单的 int,每按下一个按钮就会递增)

一个被很好地锁定并具有以下方法的 ImageMonitor:

  • 添加要渲染的图像(使用当前计数器)->每次按下按钮都会发生这种情况
  • 检索应该渲染的图像(包括图像的计数器)
  • 处理渲染图像

现在我们需要一个持续工作的后台线程,它循环并在每次迭代中检查 ImageMonitor 是否有要处理的最新图像,处理图像并将其返回给 ImageMonitor(包括计数器)

当 ImageMonitor 从后台渲染器获取渲染图像时,它可以检查图像是否具有正确的计数器值,如果是,则可以将当前图像与渲染图像交换

这个解决方案显然有点复杂。但是,它应该可以工作。我对其他(更简单的)解决方案感兴趣。

祝你好运

于 2013-02-21T07:18:17.877 回答