Note that there are some explanatory texts on larger screens.

plurals
  1. POC# threading and Windows Forms
    text
    copied!<p>Is my approach to a responsive GUI with a background process correct? If not, please please critique and offer improvements. In particular, indicate what code could potentially suffer from a deadlock or race condition.</p> <p>The worker thread needs to be able to be cancelled and report it's progress. I didn't use a BackgroundWorker because all the examples I've seen have the Process code on the Form itself, rather than a separate object. I thought about inheriting the LongRunningProcess for BackgroundWorker but I figured that would introduce unnecessary methods on the object. Ideally, I'd prefer not to have a Form reference to the process ("_lrp"), but I don't see how it would then be possible to cancel the process, unless I have an event on the LRP that checks a flag on the caller, but that seems unnecessarily complex and possibly even wrong.</p> <p>Windows Form (Edit: moved *.EndInvoke calls to the callback)</p> <pre><code>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)(() =&gt; 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&lt;IAsyncResult&gt;(TransferEnded), asyncResult); } else { _startInvoker.EndInvoke(asyncResult); _started = false; _lrp = null; } } private void Stopped(IAsyncResult asyncResult) { if (this.InvokeRequired) { this.BeginInvoke(new Action&lt;IAsyncResult&gt;(Stopped), asyncResult); } else { _stopInvoker.EndInvoke(asyncResult); _lrp = null; } } private void _lrp_ProgressChanged(object sender, ProgressChangedEventArgs e) { // Update the progress // if (progressBar.InvokeRequired) etc... } } </code></pre> <p>Background Process:</p> <pre><code>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 &lt; LARGE_NUMBER; ++i) { if (this.CancellationPending) break; // Do work.... // ... // ... // Update progress this.ReportProgress(percentageComplete); // Allow other threads to run Thread.Sleep(0) } return true; } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload