c# backgroundworker 和部分 class
c# backgroundworker and partial class
我在执行从 stackowerflow 获得的关于终止后台工作进程的代码时遇到问题。
我的代码如下:
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using GluthGUI.Classes.XMLprofile;
using System.Xml.Linq;
using System.ComponentModel;
namespace Solution
{
partial class SolGUI : Form
{
private void startButton_Click(object sender, EventArgs e)
{
backgroundWorker1 = new AbortableBackgroundWorker();
if (startButton.Text == "Start")
{
XMLParsing();
DisableTextFields();
backgroundWorker1.RunWorkerAsync();
startButton.Text = "Stop";
}
else if (startButton.Text == "Stop")
{
if (backgroundWorker1.IsBusy == true)
{
backgroundWorker1.Abort(); //error Abort() is not declared?!?!
backgroundWorker1.Dispose();
}
startButton.Text = "Start";
DisableTextFields();
}
}
}
这是一个部分 class,它将终止 backgroundworker:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Threading;
namespace Solution
{
public class AbortableBackgroundWorker : BackgroundWorker
{
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
}
}
我的问题是来自部分 class 的 Abort() 方法在具有相同命名空间的其他 class 中不可见。
问题是您正在定义类型为 BackgroundWorker
的 backgroundWorker1
,因此您无权访问 AbortableBackgroundWorker
[=33= 中定义的自定义方法].
直接向您的设计器添加 AbortableBackgroundWorker
,或在您的表单中手动声明 AbortableBackgroundWorker
:
partial class SolGUI : Form
{
AbortableBackgroundWorker backgroundWorker1 = new AbortableBackgroundWorker();
您还需要确保从按钮点击事件中删除这行代码:
backgroundWorker1 = new AbortableBackgroundWorker();
因为这会导致每次单击时都设置一个新实例,并且您永远无法访问原始实例以中止它。您也不应在任何时候处置该对象,因为您将希望在再次启动该过程时重用它,因此删除:
backgroundWorker1.Dispose();
您还需要连接您正在使用的任何事件,例如 DoWork
。你应该在你的表单构造函数中这样做:
backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
我不打算详细说明您是否应该中止线程,其他人已经评论过,如果您想听从他们的建议并进行一些研究,那是您的选择。我只是简单地回答了手头的问题。不过就个人而言,我会使用内置的取消方法。
变量 backgroundWorker1
已被定义为 BackgroundWorker
而它应该在您的部分 class.[=13= 的另一部分被定义为 AbortableBackgroundWorker
]
也许您可以在解决方案资源管理器中以 SolGUI.Desinger.cs 的形式找到它。
backgroundWorker1 = new AbortableBackgroundWorker();
这一行就是问题所在,每次单击按钮都会创建一个新的后台工作者实例。
你应该定义是否在方法之外
应该没问题
除非DoWork
事件的订阅方法是一个插件或者你无法维护的第三方代码,否则通常是very bad idea直接中止一个线程。
当您单击“停止”按钮时,您必须将取消请求传递给您的工作对象;否则,它永远不会被通知。 BackgroundWorker
有一个 CancelAsync
方法,它不执行任何操作,只是简单地设置 BackgroundWorker.CancellationPending
属性,通知 BackgroundWorker 的消费者(UI,不是执行的任务)你的任务已经被取消了。
所以你需要什么:
MyWorkerObject myObject;
// This method is executed on the worker thread. Do not access your controls
// in the main thread from here directly.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
myObject = new MyWorkerObject();
// The following line is a blocking operation in this thread.
// The user acts in the UI thread, not here, so you cannot do here
// anything but wait.
myObject.DoWork();
// Now DoWork is finished. Next line is needed only to notify
// the caller of the event whether a cancel has happened.
if (backgroundWorker1.CancellationPending)
e.Cancel = true;
myObject = null;
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
// You must notify your worker object as well.
// Note: Now you access the worker object from the main thread!
// Note2: It would be possible to pass the worker to your object
// and poll the backgroundWorker1.CancellationPending from there,
// but that would be a nasty pattern. BL objects should not
// aware of the UI components.
myObject.CancelWork();
}
}
您应该如何实施通知:
public class MyWorkerObject
{
// normally you should use locks to access fields from different threads
// but if you just set a bool from one thread and read it from another,
// then it is enough to use a volatile field.
private volatile bool isCancelRequested;
// this will be called from the main thread
public void CancelWork()
{
isCancelRequested = true;
}
// This method is called from the worker thread.
public void DoWork()
{
// Make sure you poll the isCancelRequested field often enough to
// react to the cancellation as soon as possible.
while (!isCancelRequested && ...)
{
// ...
}
}
}
我在执行从 stackowerflow 获得的关于终止后台工作进程的代码时遇到问题。
我的代码如下:
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using GluthGUI.Classes.XMLprofile;
using System.Xml.Linq;
using System.ComponentModel;
namespace Solution
{
partial class SolGUI : Form
{
private void startButton_Click(object sender, EventArgs e)
{
backgroundWorker1 = new AbortableBackgroundWorker();
if (startButton.Text == "Start")
{
XMLParsing();
DisableTextFields();
backgroundWorker1.RunWorkerAsync();
startButton.Text = "Stop";
}
else if (startButton.Text == "Stop")
{
if (backgroundWorker1.IsBusy == true)
{
backgroundWorker1.Abort(); //error Abort() is not declared?!?!
backgroundWorker1.Dispose();
}
startButton.Text = "Start";
DisableTextFields();
}
}
}
这是一个部分 class,它将终止 backgroundworker:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Threading;
namespace Solution
{
public class AbortableBackgroundWorker : BackgroundWorker
{
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
}
}
我的问题是来自部分 class 的 Abort() 方法在具有相同命名空间的其他 class 中不可见。
问题是您正在定义类型为 BackgroundWorker
的 backgroundWorker1
,因此您无权访问 AbortableBackgroundWorker
[=33= 中定义的自定义方法].
直接向您的设计器添加 AbortableBackgroundWorker
,或在您的表单中手动声明 AbortableBackgroundWorker
:
partial class SolGUI : Form
{
AbortableBackgroundWorker backgroundWorker1 = new AbortableBackgroundWorker();
您还需要确保从按钮点击事件中删除这行代码:
backgroundWorker1 = new AbortableBackgroundWorker();
因为这会导致每次单击时都设置一个新实例,并且您永远无法访问原始实例以中止它。您也不应在任何时候处置该对象,因为您将希望在再次启动该过程时重用它,因此删除:
backgroundWorker1.Dispose();
您还需要连接您正在使用的任何事件,例如 DoWork
。你应该在你的表单构造函数中这样做:
backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
我不打算详细说明您是否应该中止线程,其他人已经评论过,如果您想听从他们的建议并进行一些研究,那是您的选择。我只是简单地回答了手头的问题。不过就个人而言,我会使用内置的取消方法。
变量 backgroundWorker1
已被定义为 BackgroundWorker
而它应该在您的部分 class.[=13= 的另一部分被定义为 AbortableBackgroundWorker
]
也许您可以在解决方案资源管理器中以 SolGUI.Desinger.cs 的形式找到它。
backgroundWorker1 = new AbortableBackgroundWorker();
这一行就是问题所在,每次单击按钮都会创建一个新的后台工作者实例。 你应该定义是否在方法之外 应该没问题
除非DoWork
事件的订阅方法是一个插件或者你无法维护的第三方代码,否则通常是very bad idea直接中止一个线程。
当您单击“停止”按钮时,您必须将取消请求传递给您的工作对象;否则,它永远不会被通知。 BackgroundWorker
有一个 CancelAsync
方法,它不执行任何操作,只是简单地设置 BackgroundWorker.CancellationPending
属性,通知 BackgroundWorker 的消费者(UI,不是执行的任务)你的任务已经被取消了。
所以你需要什么:
MyWorkerObject myObject;
// This method is executed on the worker thread. Do not access your controls
// in the main thread from here directly.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
myObject = new MyWorkerObject();
// The following line is a blocking operation in this thread.
// The user acts in the UI thread, not here, so you cannot do here
// anything but wait.
myObject.DoWork();
// Now DoWork is finished. Next line is needed only to notify
// the caller of the event whether a cancel has happened.
if (backgroundWorker1.CancellationPending)
e.Cancel = true;
myObject = null;
}
private void btnCancel_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
// You must notify your worker object as well.
// Note: Now you access the worker object from the main thread!
// Note2: It would be possible to pass the worker to your object
// and poll the backgroundWorker1.CancellationPending from there,
// but that would be a nasty pattern. BL objects should not
// aware of the UI components.
myObject.CancelWork();
}
}
您应该如何实施通知:
public class MyWorkerObject
{
// normally you should use locks to access fields from different threads
// but if you just set a bool from one thread and read it from another,
// then it is enough to use a volatile field.
private volatile bool isCancelRequested;
// this will be called from the main thread
public void CancelWork()
{
isCancelRequested = true;
}
// This method is called from the worker thread.
public void DoWork()
{
// Make sure you poll the isCancelRequested field often enough to
// react to the cancellation as soon as possible.
while (!isCancelRequested && ...)
{
// ...
}
}
}