c# неожиданное поведение backgroundWorker - PullRequest
0 голосов
/ 17 февраля 2020

Я прикрепил компонент backgroundWorker к своей основной форме, который выполняет параллельную задачу захвата экрана для анимированного GIF. У функции работника есть время l oop, которое выполняется до тех пор, пока я не использую CancelAsync() на работнике, после чего оно выходит из l oop, выполняет некоторые другие действия, такие как сохранение файла GIF и т. Д., И возвращает некоторые результаты поток пользовательского интерфейса.

private bool capturing = false;

public MainForm()
{
    InitializeComponent();
    backgroundWorker.DoWork += backgroundWorker_DoWork;
    backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
    backgroundWorker.WorkerSupportsCancellation = true;
}

private void captureBtn_Click(object sender, EventArgs e)
{
    Debug.WriteLine("Button clicked");
    if (capturing) { return; }
    if (!backgroundWorker.IsBusy)
    {
        backgroundWorker.RunWorkerAsync();
    }
}

private void stopCaptureBtn_Click(object sender, EventArgs e)
{
    if (backgroundWorker.IsBusy)
    {
        backgroundWorker.CancelAsync();
    }
}

private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    capturing = true;
    Debug.WriteLine("DoWork running");

    while (!backgroundWorker.CancellationPending)
    {
        Debug.WriteLine("Capturing frame {0}", frames);
        //do the capturing to memory stream
    }

    Debug.WriteLine("DoWork cancelled");

    //do some other things like saving the gif etc
    e.Result = someResult;
}

private void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    Debug.WriteLine("RunWorkerCompleted running");
    capturing = false;
    //does something with the e.Result
}

Мой вывод на консоль во время обычного теста выглядит примерно так:

Button clicked
DoWork running
Capturing frame 0
Capturing frame 1
Capturing frame 2
Capturing frame 3
Capturing frame 4
Capturing frame 5
Cancel button clicked
DoWork cancelled
The thread 0x2e4c has exited with code 0 (0x0).
DoWork running
DoWork cancelled
The thread 0x1010 has exited with code 0 (0x0).
RunWorkerCompleted running

Кажется, что функция выполняется дважды, я вижу, как выходят 2 отдельных потока, и также я не получаю никаких результатов от захвата. Если я установлю точку останова внутри функции backgroundWorker_DoWork и продолжу позже, первый запуск выполняет захват как обычно. Что может происходить?

Ответы [ 2 ]

1 голос
/ 18 февраля 2020

Он вызывается дважды, потому что связывает событие во второй раз сразу после InitializeComponent (). Просто прокомментируйте эти строки, и это должно работать нормально. Вот тот же пример без повторного запуска.

Пример вывода ... ... ... Захват кадра 2632 Захват кадра 2633 Захват кадра 2634 DoWork отменен RunWorkerCompleted running

public partial class Form1 : Form
    {
        private bool capturing = false;
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerSupportsCancellation = true;

  // Don't need to re-bind
            //backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            //backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
            //backgroundWorker1.WorkerSupportsCancellation = true;
        }



private void captureBtn_Click(object sender, EventArgs e)
        {
            Debug.WriteLine("Button clicked");
            if (capturing) { return; }
            if (!backgroundWorker1.IsBusy)
            {
                backgroundWorker1.RunWorkerAsync();
            }
        }

        private void stopCaptureBtn_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.IsBusy)
            {
                backgroundWorker1.CancelAsync();
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            capturing = true;
            Debug.WriteLine("DoWork running");
            int frames = 1;

            while (!backgroundWorker1.CancellationPending)
            {
                Debug.WriteLine("Capturing frame {0}", frames);
                //do the capturing to memory stream
                frames++;
            }

            Debug.WriteLine("DoWork cancelled");

            //do some other things like saving the gif etc
            //e.Result = someResult;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Debug.WriteLine("RunWorkerCompleted running");
            capturing = false;
            //does something with the e.Result
        }

0 голосов
/ 17 февраля 2020

Поток 0x3108 вышел с кодом 0 (0x0), что означает отсутствие ошибки. При чтении или записи больших данных вы должны разделить их на части. В противном случае вы не сможете прогрессировать. Ваше текущее время l oop отключено при записи в поток памяти. поэтому ваш метод backgroundWorker_DoWork должен выглядеть следующим образом:

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = (BackgroundWorker)sender;

        // this is an example file
        string filePath = @"C:\file.gif";

        // it determines how many bytes of data will be received from the stream each time.
        byte[] buffer = new byte[4096];
        int byteCount = 0;

        // we are preparing to read stream.
        using (FileStream fs = new FileStream(filePath, FileMode.Open, System.IO.FileAccess.Read))
        using (MemoryStream ms = new MemoryStream())
        {
            // read the stream bytes and fill buffer
            while ((byteCount = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                // if the task was canceled
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }

                // write buffer to memory stream
                ms.Write(buffer, 0, byteCount);
            }
        }
    }
...