c# 意外的 backgroundWorker 行为
c# unexpected backgroundWorker behaviour
我已将 backgroundWorker 组件附加到我的主窗体,该组件 运行 是为动画 gif 捕获屏幕的并行任务。工作人员的函数有一个 运行s 的 while 循环,直到我在工作人员上使用 CancelAsync()
,此时它退出循环,做一些其他事情,比如保存 gif 文件等等 returns UI 线程的一些结果。
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
函数似乎 运行ning 两次,我可以看到 2 个单独的线程退出,而且我似乎没有从捕获中获得任何结果。如果我在 backgroundWorker_DoWork 函数内设置断点并稍后继续,第一个 运行 会正常进行捕获。可能发生了什么?
线程 0x3108 已退出,代码 0 (0x0) 表示没有错误。
在读取或写入大数据时,您应该将其分成几部分。否则,你无法进步。写入内存流时,您当前的 while 循环被禁用。
所以你的 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);
}
}
}
它被调用两次,因为在 InitializeComponent() 之后第二次绑定事件。
只需评论这些行,它应该可以正常工作。
这是没有 运行 两次问题的相同示例。
示例输出
...
...
...
捕获帧 2632
捕获帧 2633
捕获帧 2634
取消工作
RunWorkerCompleted 运行
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
}
我已将 backgroundWorker 组件附加到我的主窗体,该组件 运行 是为动画 gif 捕获屏幕的并行任务。工作人员的函数有一个 运行s 的 while 循环,直到我在工作人员上使用 CancelAsync()
,此时它退出循环,做一些其他事情,比如保存 gif 文件等等 returns UI 线程的一些结果。
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
函数似乎 运行ning 两次,我可以看到 2 个单独的线程退出,而且我似乎没有从捕获中获得任何结果。如果我在 backgroundWorker_DoWork 函数内设置断点并稍后继续,第一个 运行 会正常进行捕获。可能发生了什么?
线程 0x3108 已退出,代码 0 (0x0) 表示没有错误。 在读取或写入大数据时,您应该将其分成几部分。否则,你无法进步。写入内存流时,您当前的 while 循环被禁用。 所以你的 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);
}
}
}
它被调用两次,因为在 InitializeComponent() 之后第二次绑定事件。 只需评论这些行,它应该可以正常工作。 这是没有 运行 两次问题的相同示例。
示例输出 ... ... ... 捕获帧 2632 捕获帧 2633 捕获帧 2634 取消工作 RunWorkerCompleted 运行
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
}