Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save udaken/70577793c63134b141b2bfe136c74f4b to your computer and use it in GitHub Desktop.
Save udaken/70577793c63134b141b2bfe136c74f4b to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
volatile bool _causeError = false;
#if false
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100; i++)
{
backgroundWorker1.ReportProgress(i);
Thread.Sleep((int)e.Argument);
if (backgroundWorker1.CancellationPending)
break;
e.Cancel = true;
}
e.Result = "OK";
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = e.Cancelled ? "" : e.Result.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy)
backgroundWorker1.CancelAsync();
else
backgroundWorker1.RunWorkerAsync(100);
}
#else
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
sealed class BackgroundTask<TResult, TProgressArg> : IDisposable
{
private CancellationTokenSource _cts;
public CancellationToken CancellationToken => _cts?.Token ?? CancellationToken.None;
public void Cancel() => _cts?.Cancel();
public event EventHandler<TProgressArg> ProgressChanged;
public event EventHandler<(bool Canceled, TResult Result, Exception Exception)> RunWorkerCompleted;
private void OnProgressChanged(TProgressArg progress)
{
if (!(_cts?.IsCancellationRequested ?? true))
{
ProgressChanged?.Invoke(this, progress);
}
}
public async void Run<TArg>(Func<CancellationToken, TArg, IProgress<TProgressArg>, Task<TResult>> action, TArg argument)
{
_cts?.Dispose();
using (_cts = new CancellationTokenSource())
{
CancellationToken ct = _cts.Token;
var progress = new Progress<TProgressArg>(OnProgressChanged);
try
{
var task = Task.Run(() =>
{
return action(ct, argument, progress);
}, ct);
RunWorkerCompleted?.Invoke(this, (false, await task.ConfigureAwait(true), null));
}
catch (OperationCanceledException oce) when (oce.CancellationToken == _cts.Token)
{
RunWorkerCompleted?.Invoke(this, (true, default, null));
}
catch (Exception ex)
{
RunWorkerCompleted?.Invoke(this, (false, default, ex));
}
}
_cts = null;
}
public void Dispose()
{
_cts?.Dispose();
}
}
sealed class ProgressArg
{
public int Percentage { get; internal set; }
}
BackgroundTask<string, ProgressArg> _task;
private void button1_Click(object sender, EventArgs e)
{
if (_task != null)
{
_task.Cancel();
_task = null;
}
else
{
_causeError = !_causeError;
_task = new BackgroundTask<string, ProgressArg>();
_task.RunWorkerCompleted += (object _, (bool cancel, string result, Exception ex) ev) =>
{
label1.Text = ev.cancel ? "" : ev.ex?.Message ?? ev.result;
_task = null;
};
_task.ProgressChanged += (object _, ProgressArg ev) =>
{
label1.Text = $"{ev.Percentage} %";
};
_task.Run(async (ct, arg, progress) =>
{
Debug.WriteLine("start...");
if (_causeError)
throw new ApplicationException();
var progressArg = new ProgressArg();
for (int i = 0; i < 100; i++)
{
progressArg.Percentage = i;
progress.Report(progressArg);
await Task.Delay(arg).ConfigureAwait(false);
ct.ThrowIfCancellationRequested();
}
Debug.WriteLine("done...");
return "OK";
}, 100);
}
}
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment