c # backgoundworker и трекбар

У меня проблема с фоновыми работниками. У меня есть трекбар, и когда пользователь меняет его значение, запускается новый фоновый рабочий. Есть список всех фоновых рабочих, и когда запускается новый, все рабочие в списке вызывают worker.CancelAsync().

Он работает, когда пользователь медленно меняет трекбар, но когда вы перемещаете его очень быстро, возникает около 20+ потоков, и требуется некоторое время, чтобы убить их в WorkerCompleted. Кроме того, в этой функции очищаются рабочие переменные (в данном случае это копия растрового изображения), поэтому 20+ рабочим требуется много памяти, и я получаю OutOfMemoryException.

Есть ли способ заблокировать количество потоков примерно до 4, и когда количество фоновых рабочих равно 4, тогда программа будет ждать, когда они будут удалены, или есть ли способ сделать это только с одним фоновым рабочим и когда трекбар значение изменено перезапускается?


Добавление нового работника:

public override void StartWorker(Bitmap bmp, bool needTempImage)
{
    if(m_workersList.Count<maxThread)
    {
        CancelAllJobs();

        // debug.Text = "locked";
        BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);

        imageDataAttributes imgAttr = new imageDataAttributes(bd.Width, bd.Height, bd.Stride, 4);

        ThreadWorker worker = new ThreadWorker(needTempImage, bd.Scan0, imgAttr);
        bmp.UnlockBits(bd);

        m_workersList.Add(worker);
        m_currentWorker = worker;
        worker.worker.WorkerSupportsCancellation = true;
        worker.worker.DoWork += WorkerDoWork;
        worker.worker.WorkerReportsProgress = report;

        if (report == true)
        {
            worker.worker.ProgressChanged += WorkerProgress;
            m_progressBar.Visible = true;
        }

        worker.worker.RunWorkerCompleted += WorkerCompleted;
        worker.worker.RunWorkerAsync(worker);

        debug.Text = "" + m_workersList.Count;
    }
    //debug.Text = "unlocked";    
}

Это отменяет:

public override void CancelAllJobs()
{
    foreach (ThreadWorker worker in m_workersList)
    {
        worker.cancelled = true;
        worker.worker.CancelAsync();
    }
    debug.Text = "" + m_workersList.Count;
}

Выполнять работу:

protected override void WorkerDoWork(object sender, DoWorkEventArgs e)
{
    ThreadWorker worker = (ThreadWorker)e.Argument;
    if (worker.worker.CancellationPending == true)
    {
        e.Cancel = true;
        worker.cancelled = true;
        return;
    }

    WorkerProcessFun(worker);
}

WorkerCompleted:

protected override void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    ThreadWorker worker = m_workersList.Find(w => w.worker == sender);

    if (!worker.cancelled && worker == m_currentWorker)
    {
        if (e.Error != null)
        {
            MessageBox.Show("Worker Thread Error " + e.Error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        else
        {
            WorkerOnCompleteFun(worker.imgDataArray);
        }
    }

    m_workersList.Remove(worker);
    worker.Clean();
    if (worker == m_currentWorker) m_currentWorker = null;
    debug.Text = "" + m_workersList.Count;
}

ProcessMainFun нужен рабочий, потому что есть проверка CancelationPending, установка e.Cancel = true; и вернуться;

private void MainProcessFun(ThreadWorker worker)
{
    Filters.Filters.AdvancedBlur(m_radius, m_sigma, worker);
}

person Smajler Kamil    schedule 19.11.2013    source источник
comment
Вы должны показать в коде то, что пробовали в данный момент, иначе вы рискуете закрыть свой вопрос.   -  person Scott Chamberlain    schedule 20.11.2013
comment
если фонового работника недостаточно, вам может потребоваться использовать пул потоков. Может быть, у вас есть собственные коллекции потоков, которыми вы управляете сами ...   -  person Noctis    schedule 20.11.2013
comment
Нет сценария, при котором ваш тест на CancellationPending внутри метода DoWork когда-либо будет истинным, вы тестируете его через микросекунду после того, как вы вызвали DoWork. Нет сценария, при котором ThreadWorker мог бы когда-либо узнать, что BackgroundWorker.CancellationPending когда-либо истинно. Так что это не сработает, бабах. Продвигайтесь вперед, не запускайте BGW, пока не пройдет какое-то время, или не добавляйте кнопку DoIt.   -  person Hans Passant    schedule 20.11.2013


Ответы (1)


Этот код программной части для кнопки и метки описывает, как можно запустить один фоновый поток, а затем перезапустить его в произвольной позиции, используя новое начальное состояние.

В этом случае я предпочитаю отправлять целое число, но вместо этого вы можете легко отправить очень сложный объект. Замените код в CodeToRunInsideBackgroundThread (состояние объекта) любым кодом, который вам нужен ... Дело в том, что вам не нужно несколько потоков.

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

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        Thread backgroundWorker = null;
        int startingThreadState = 0;

        private void button1_Click(object sender, EventArgs e)
        {
            startingThreadState += 100;
            if (backgroundWorker == null || !backgroundWorker.IsAlive)
            {
                InitThread();
                backgroundWorker.Start(startingThreadState);
            }
            else
            {
                backgroundWorker.Abort(startingThreadState);
            }
        }

        private void InitThread()
        {
            backgroundWorker = new Thread(new ParameterizedThreadStart((state)=>
                {
                    while (true)
                    {
                        try
                        {
                            CodeToRunInsideBackgroundThread(state);
                            break;//while(true)
                        }
                        catch (ThreadAbortException ex)
                        {
                            System.Threading.Thread.ResetAbort();
                            state = startingThreadState;// state available in ex.Data here?
                        }
                    }
                }));
            backgroundWorker.IsBackground = true;
        }

        private void CodeToRunInsideBackgroundThread(object state)
        {
            for (int i = (int)state; i < (int)state + 3; i++)
            {
                System.Threading.Thread.Sleep(1000);
                this.Invoke(
                    new Action(() =>
                    {
                        label1.Text = i.ToString();
                    })
                );
            }
        }
    }
}
person Mattias Åslund    schedule 19.11.2013
comment
Я постараюсь написать свой код, используя Thread вместо BackgroundWorker, потому что Thread можно убить с помощью Abort, и я думаю, что это поможет мне. У Bgworker есть только CancelAsync, который, я думаю, сразу не убивает. - person Smajler Kamil; 20.11.2013