gpt4 book ai didi

C# 线程和 Windows 窗体

转载 作者:行者123 更新时间:2023-11-30 12:18:57 25 4
gpt4 key购买 nike

我对具有后台进程的响应式 GUI 的方法是否正确?如果没有,请批评并提供改进。特别是指出哪些代码可能会遇到死锁或竞争条件。

工作线程需要能够被取消并报告它的进度。我没有使用 BackgroundWorker,因为我见过的所有示例都在 Form 本身上有 Process 代码,而不是一个单独的对象。我考虑过为 BackgroundWorker 继承 LongRunningProcess,但我认为这会在对象上引入不必要的方法。理想情况下,我不希望有一个 Form 引用流程(“_lrp”),但我不知道如何取消流程,除非我在 LRP 上有一个检查标志的事件在调用者上,但这似乎不必要地复杂,甚至可能是错误的。

Windows 窗体(编辑:将 *.EndInvoke 调用移动到回调)

public partial class MainForm : Form
{
MethodInvoker _startInvoker = null;
MethodInvoker _stopInvoker = null;
bool _started = false;

LongRunningProcess _lrp = null;

private void btnAction_Click(object sender, EventArgs e)
{
// This button acts as a Start/Stop switch.
// GUI handling (changing button text etc) omitted
if (!_started)
{
_started = true;
var lrp = new LongRunningProcess();

_startInvoker = new MethodInvoker((Action)(() => Start(lrp)));
_startInvoker.BeginInvoke(new AsyncCallback(TransferEnded), null);
}
else
{
_started = false;
_stopInvoker = new MethodInvoker(Stop);
_stopInvoker.BeginInvoke(Stopped, null);
}
}

private void Start(LongRunningProcess lrp)
{
// Store a reference to the process
_lrp = lrp;

// This is the same technique used by BackgroundWorker
// The long running process calls this event when it
// reports its progress
_lrp.ProgressChanged += new ProgressChangedEventHandler(_lrp_ProgressChanged);
_lrp.RunProcess();
}

private void Stop()
{
// When this flag is set, the LRP will stop processing
_lrp.CancellationPending = true;
}

// This method is called when the process completes
private void TransferEnded(IAsyncResult asyncResult)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action<IAsyncResult>(TransferEnded), asyncResult);
}
else
{
_startInvoker.EndInvoke(asyncResult);
_started = false;
_lrp = null;
}
}

private void Stopped(IAsyncResult asyncResult)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new Action<IAsyncResult>(Stopped), asyncResult);
}
else
{
_stopInvoker.EndInvoke(asyncResult);
_lrp = null;
}
}

private void _lrp_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update the progress
// if (progressBar.InvokeRequired) etc...
}
}

后台进程:

public class LongRunningProcess
{
SendOrPostCallback _progressReporter;
private readonly object _syncObject = new object();
private bool _cancellationPending = false;

public event ProgressChangedEventHandler ProgressChanged;

public bool CancellationPending
{
get { lock (_syncObject) { return _cancellationPending; } }
set { lock (_syncObject) { _cancellationPending = value; } }
}

private void ReportProgress(int percentProgress)
{
this._progressReporter(new ProgressChangedEventArgs(percentProgress, null));
}

private void ProgressReporter(object arg)
{
this.OnProgressChanged((ProgressChangedEventArgs)arg);
}

protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
if (ProgressChanged != null)
ProgressChanged(this, e);
}

public bool RunProcess(string data)
{
// This code should be in the constructor
_progressReporter = new SendOrPostCallback(this.ProgressReporter);

for (int i = 0; i < LARGE_NUMBER; ++i)
{
if (this.CancellationPending)
break;

// Do work....
// ...
// ...

// Update progress
this.ReportProgress(percentageComplete);

// Allow other threads to run
Thread.Sleep(0)
}

return true;
}
}

最佳答案

我喜欢把后台进程分离在一个单独的对象中。但是,我的印象是您的 UI 线程在后台进程完成之前一直处于阻塞状态,因为您在同一个按钮处理程序中调用了 BeginInvoke 和 EndInvoke。

MethodInvoker methodInvoker = new MethodInvoker((Action)(() => Start(lrp)));
IAsyncResult result = methodInvoker.BeginInvoke(new AsyncCallback(TransferEnded), null);
methodInvoker.EndInvoke(result);

还是我遗漏了什么?

关于C# 线程和 Windows 窗体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/986384/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com