Threading multiple async calls - multithreading

Part of my Silverlight application requires data from three service requests. Up until now I've been chaining the requests so as one completes the other starts... until the end of the chain where I do what I need to do with the data.
Now, I know thats not the best method(!). I've been looking at AutoResetEvent (link to MSDN example) to thread and then synchronize the results but cannot seem to get this to work with async service calls.
Does anyone have any reason to doubt this method or should this work? Code samples gratefully received!

Take a look at this example:
Will fire Completed event and print 'done' to Debug Output once both services returned.
Key thing is that waiting for AutoResetEvents happens in background thread.
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
Completed += (s, a) => { Debug.WriteLine("done"); };
wrk.DoWork += (s, a) =>
{
Start();
};
wrk.RunWorkerAsync();
}
public event EventHandler Completed;
private void Start()
{
auto1.WaitOne();
auto2.WaitOne();
Completed(this, EventArgs.Empty);
}
public AutoResetEvent auto1 = new AutoResetEvent(false);
public AutoResetEvent auto2 = new AutoResetEvent(false);
BackgroundWorker wrk = new BackgroundWorker();
private void Button_Click(object sender, RoutedEventArgs e)
{
ServiceReference1.Service1Client clien = new SilverlightAsyncTest.ServiceReference1.Service1Client();
clien.DoWorkCompleted += new EventHandler<SilverlightAsyncTest.ServiceReference1.DoWorkCompletedEventArgs>(clien_DoWorkCompleted);
clien.DoWork2Completed += new EventHandler<SilverlightAsyncTest.ServiceReference1.DoWork2CompletedEventArgs>(clien_DoWork2Completed);
clien.DoWorkAsync();
clien.DoWork2Async();
}
void clien_DoWork2Completed(object sender, SilverlightAsyncTest.ServiceReference1.DoWork2CompletedEventArgs e)
{
Debug.WriteLine("2");
auto1.Set();
}
void clien_DoWorkCompleted(object sender, SilverlightAsyncTest.ServiceReference1.DoWorkCompletedEventArgs e)
{
Debug.WriteLine("1");
auto2.Set();
}
}

It could be done using the WaitHandle in the IAsyncResult returned by each async method.
The code is simple. In Silverlight I just do 10 service calls that will add an item to a ListBox. I'll wait until all the service calls end to add another message to the list (this has to run in a different thread to avoid blocking the UI). Also note that adding items to the list have to be done through the Dispatcher since they will modify the UI. There're a bunch of lamdas, but it's easy to follow.
public MainPage()
{
InitializeComponent();
var results = new ObservableCollection<string>();
var asyncResults = new List<IAsyncResult>();
resultsList.ItemsSource = results;
var service = new Service1Client() as Service1;
1.To(10).Do(i=>
asyncResults.Add(service.BeginDoWork(ar =>
Dispatcher.BeginInvoke(() => results.Add(String.Format("Call {0} finished: {1}", i, service.EndDoWork(ar)))),
null))
);
new Thread(()=>
{
asyncResults.ForEach(a => a.AsyncWaitHandle.WaitOne());
Dispatcher.BeginInvoke(() => results.Add("Everything finished"));
}).Start();
}
Just to help with the testing, this is the service
public class Service1
{
private const int maxMilliSecs = 500;
private const int minMillisSecs = 100;
[OperationContract]
public int DoWork()
{
int millisSecsToWait = new Random().Next(maxMilliSecs - minMillisSecs) + minMillisSecs;
Thread.Sleep(millisSecsToWait);
return millisSecsToWait;
}
}

Related

How to properly close a flowable and close response body using rxjava and retrofit

I am attempting to close a stream coming from an http request using Retrofit and rxjava, either because it timedOut, or because I need to change details that went into the request. Both appear to work perfectly, as when I cancel subscription I get the doOnCancel debug message and when doOnNext is completed I get the doOnTerminate message. I also do not receive inputLines from multiple threads. However, my thread count rises every single time either of the above actions happen. It appears that responsebody.close is not releasing their resources and therefore the thread is not dying (I also have gotten error messages along the lines of "OKHTTP leaked. did you close youre responseBody?")
Does anyone have any suggestions?
public boolean closeSubscription() {
flowableAlive = false;
subscription.cancel();
return true;
}
public void subscribeToFlowable() {
streamFlowable.observeOn(Schedulers.newThread()).subscribeOn(Schedulers.newThread())
.doOnTerminate(() -> log.debug("TERMINATED")).doOnCancel(() -> log.debug("FLOWABLE CANCELED"))
.subscribe(new Subscriber<ResponseBody>() {
#Override
public void onSubscribe(Subscription s) {
subscription = s;
subscription.request(Long.MAX_VALUE);
}
#Override
public void onNext(ResponseBody responseBody) {
log.debug("onNext called");
String inputLine;
try (InputStream inputStream = responseBody.byteStream()) {
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
while (flowableAlive && ((inputLine = br.readLine()) != null)) {
log.debug("stream receive input line for thread " + name);
log.debug(inputLine);
}
} catch (IOException e) {
log.debug("error occurred");
log.debug(e.getMessage());
}
}
#Override
public void onError(Throwable t) {
log.debug("error");
flowableAlive = false;
}
#Override
public void onComplete() {
log.debug("completed");
closeSubscription();
flowableAlive = false;
}
});
}
The result of subscribe() is Disposable object. You should store it as a filed and call Disposable.dispose() on it later as shown here:
https://proandroiddev.com/disposing-on-android-the-right-way-97bd55cbf970
Tour OkHttp call will be interrupted properly because dispose() interrupts thread on which the call runs and OkHttp checks regularly if Thread was interrupted to stop transfer when that happened - it's called cooperative cancelling/interruption.

An object reference is required for the non-static field method or property/field initializer cannot reference the non-static field method or property

I was making a program and I stumbled across this two-in-one problem, where the first problem leads to the other. I have not yet found a question where someone had both problems leading into eachother. I'm still learing, and have learned a lot from other problems I had, but I can't find a solution to this problem.
It has to do with threading. I want to make a thread, that can place something in a rich textbox every second or so, while I can still press buttons to start and stop it. But to make a function that a thread can run, I need to make the function static. Otherwise I'll get the error "A field initializer cannot reference the non-static field, method, or property". But when a function is static, it cannot acces any of the created items, like richTextBox1. Because if I try to change it's text, I get the error "Error 1 An object reference is required for the non-static field, method, or property". And if I fix this by removing static, the thread will not work.
I made a demo program that is smaller than the full one, but has the same problem. Button1 is the button to start the thread, Button2 is the one to stop it.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace threading_non_static_problem_demo
{
public partial class Form1 : Form
{
static Thread thr = new Thread(new ThreadStart(demofunc));
int checkthr = 0; //int to check if the thread has been running before (I like to do things like this)
int ifthrrun = 0; //int to check if the thread is running
public Form1()
{
InitializeComponent();
button2.Enabled = false; // so you can't click the "stop" button if nothing is running
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
static void demofunc()
{
while (true)
{
Thread.Sleep(1000);
richTextBox1.Text = richTextBox1.Text + "text added"; // <-- here's the problem
MessageBox.Show("tried to add text"); // you can use this messagebox to check if the thread is working correctly
}
}
private void button1_Click(object sender, EventArgs e)
{
if (checkthr == 0) // check if the thread has run before, 0 is no, 1 is yes, and then start or resume it
{
thr.Start();
ifthrrun = 1;
button2.Enabled = true;
button1.Enabled = false;
}
else if (checkthr == 1)
{
thr.Resume();
ifthrrun = 1;
button2.Enabled = true;
button1.Enabled = false;
}
}
private void button2_Click(object sender, EventArgs e)
{
thr.Suspend();
checkthr = 1;
ifthrrun = 0;
button2.Enabled = false;
button1.Enabled = true;
}
protected override void OnFormClosing(FormClosingEventArgs e) // if the program is closing, check the thread's state and act accordingly
{
if (ifthrrun == 0)
{
if (checkthr == 1)
{
thr.Resume();
thr.Abort();
}
else if (checkthr == 0)
{
}
}
else if (ifthrrun == 1)
{
thr.Abort();
}
}
}
}
To use this code just creat a forms application, add two buttons, and a rich text box, it should work.
Thank you in advance for you answers.
But to make a function that a thread can run, I need to make the
function static.
Get rid of the static declarations and move initialization of your "thr" variable to the constructor like this:
Thread thr;
public Form1()
{
InitializeComponent();
button2.Enabled = false; // so you can't click the "stop" button if nothing is running
thr = new Thread(new ThreadStart(demofunc));
Control.CheckForIllegalCrossThreadCalls = false;
}
private void demofunc()
{
while (true)
{
Thread.Sleep(1000);
richTextBox1.Text = richTextBox1.Text + "text added"; // <-- problem "solved" by disabling Control.CheckForIllegalCrossThreadCalls
}
}
But ignore the above "fix" because using Suspend()/Resume() is not recommended.
See Pausing and Resuming Threads:
Important
Starting with the .NET Framework version 2.0, the Thread.Suspend and
Thread.Resume methods are marked obsolete and will be removed in a
future release.
The Thread.Suspend and Thread.Resume methods are not
generally useful for applications and should not be confused with
synchronization mechanisms. Because Thread.Suspend and Thread.Resume
do not rely on the cooperation of the thread being controlled, they
are highly intrusive and can result in serious application problems
like deadlocks (for example, if you suspend a thread that holds a
resource that another thread will need).
One way to be able to pause/resume your loop would be to use a ManualResetEvent like this:
Thread thr;
ManualResetEvent mre = new ManualResetEvent(false);
public Form1()
{
InitializeComponent();
button2.Enabled = false; // so you can't click the "stop" button if nothing is running
thr = new Thread(new ThreadStart(demofunc));
}
private void demofunc()
{
while (!this.IsDisposed && !this.Disposing)
{
Thread.Sleep(1000);
if (!this.IsDisposed && !this.Disposing)
{
this.Invoke((MethodInvoker)delegate {
richTextBox1.AppendText("text added");
});
}
mre.WaitOne();
}
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
mre.Set();
if (!thr.IsAlive)
{
thr.Start();
}
button2.Enabled = true;
}
private void button2_Click(object sender, EventArgs e)
{
button2.Enabled = false;
mre.Reset();
button1.Enabled = true;
}
protected override void OnFormClosing(FormClosingEventArgs e) // if the program is closing, check the thread's state and act accordingly
{
mre.Set(); // make sure the loop continues so it can drop out
}
}

J2ME - How to make a thread return a value and after that thread is finished, use the return value in other operations?

I got some questions regarding the use of threads, specially when you have to wait for a thread to be finished so you can perform other operations.
In my app, I use threads for operations such as http connections or when I read from or write to a RecordStore.
For example in the following class that I use to initialize my thread, I retrieve some customers from a webservice using the method called HttpQueryCustomers.
public class thrLoadCustomers implements Runnable {
private RMSCustomer mRMSCustomer;
private String mUrl;
public thrLoadCustomers(RMSCustomer rmsCust, String url) {
mRMSCustomer = rmsCust;
mUrl = url;
}
public void run() {
String jsonResultados = "";
try {
jsonResultados = HttpQueryCustomers();
} catch (IOException ex) {
//How to show a message from here??
} catch (SecurityException se) {
//How to show a message here??
} catch (NullPointerException npe) {
//How to show a message from here??
}
if (!jsonResultados.equals("")) {
try {
mRMSCustomer.save(jsonResultados);
} catch (RecordStoreException ex) {
//How to show a message from here???
}
}
}
public String HttpQueryCustomers() throws IOException,SecurityException,NullPointerException {
StringBuffer stringBuffer = new StringBuffer();
HttpConnection hc = null;
InputStream is = null;
System.out.println(mUrl);
try {
hc = (HttpConnection) Connector.open(mUrl);
if (hc.getResponseCode() == HttpConnection.HTTP_OK) {
is = hc.openInputStream();
int ch;
while ((ch = is.read()) != -1) {
stringBuffer.append((char) ch);
}
}
} finally {
is.close();
hc.close();
}
String jsonData = stringBuffer.toString();
return jsonData.toString();
}
}
Notice in the above class that I pass a parameter called rmsCust of the type RMSCustomer
RMSCustomer is a class that I use to handle all the operations related to RMS:
public class RMSCustomer {
private String mRecordStoreName;
private Customer[] mCustomerList;
public RMSCustomer(String recordStoreName) {
mRecordStoreName = recordStoreName;
}
public Customer[] getCustomers() {
return mCustomerList;
}
public Customer get(int index) {
return mCustomerList[index];
}
public void save(String data) throws RecordStoreException,JSONException,NullPointerException {
RecordStore rs = null;
int idNuevoRegistro;
String stringJSON;
try {
rs = RecordStore.openRecordStore(mRecordStoreName, true);
JSONArray js = new JSONArray(data);
//Set the size of the array
mCustomerList = new Customer[js.length()];
for (int i = 0; i < js.length(); i++) {
JSONObject jsObj = js.getJSONObject(i);
stringJSON = jsObj.toString();
idNuevoRegistro = addRecord(stringJSON, rs);
//Add a new Customer to the array
mCustomerList[i] = initializeCustomer(stringJSON, idNuevoRegistro);
}
} finally {
if (rs != null) {
rs.closeRecordStore();
}
}
}
public int addRecord(String stringJSON, RecordStore rs) throws JSONException,RecordStoreException {
byte[] raw = stringJSON.getBytes();
int idNuevoRegistro = rs.addRecord(raw, 0, raw.length);
return idNuevoRegistro;
}
public Customer initializeCustomer(String stringJSON, int idRecord) throws JSONException {
Customer c = new Customer();
JSONObject jsonObj = new JSONObject(stringJSON);
// Set Customer properties
//...
return c;
}
}
This class is used to show a list of customer and ,as you can see, it extends the List class and receives an array of Customers as a parameter.
public class ListCustomers extends List {
private final Customer[] mData;
public static ListCustomers create(Customer[] data) {
int i = 0;
for (; i < data.length; i++) {
if (data[i] == null) {
break;
}
}
String[] names = new String[i];
for (int j = 0; j < i; j++) {
names[j] = data[j].name;
}
return new ListCustomers(names, data);
}
protected ListCustomers(String names[], Customer[] data) {
super("List of Customer", IMPLICIT, names, null);
mData = data;
}
public Customer getSelectedObject() {
return mData[this.getSelectedIndex()];
}
}
Finally this is how I call the thread from the MIDlet (using all the 3 previous classes) when I want to show a List of Customers:
private void showCustomerList(String url) {
showWaitForm();
if (scrCustomerList == null) {
rmsCustomers = new RMSCustomer("rmsCustomers");
thrLoadCustomers load = new thrLoadCustomers(rmsCustomers, url);
Thread t = new Thread(load);
t.start();
try {
t.join();
} catch (InterruptedException ex) {
}
scrCustomerList = ListCustomers.create(rmsCustomers.getCustomers());
scrCustomerList.addCommand(cmdSelect);
scrCustomerList.addCommand(cmdBack);
scrCustomerList.setCommandListener(this);
}
mDisplay.setCurrent(scrCustomerList);
}
Now here's the problems I have :
The showWaitForm() doesn't work (it sets a form with a Gauge as the
Current form)
I don't know how to show all the exceptions that might be thrown from
within the thrLoadCustomers class.
I don't know whether using t.join() is the best choice
The last question is about something the book I'm reading says :
Threads, in particular, can be a scarce commodity. The MSA
specification requires that an application must be allowed to create
ten threads. Just because you can doesn’t mean you should. In general,
try to use the fewest resources possible so that your application will
run as smoothly as possible
This is the first time a use threads, and in my app I might have up to 10 threads (classes). However, I will only execute once thread at the time, will I be going against what the previous quotation says??
I hope I'm not asking too many questions. Thank you very much for your help.
P.D Much of the code I posted here wouldn't have been possible with the help of Gregor Ophey
Question #1 is about a different problem not related to threading, and for which very little code is shown. I'd suggest you to post a new dedicated question with proper explanation of the issue.
Questions #2 and #3: You could define a wrapper class like this:
public class WSResult {
private boolean success; //true if the WS call went ok, false otherwise
private String errorMessage; //Error message to display if the WS call failed.
private Object result; //Result, only if the WS call succeeded.
private boolean completed = false;
//TODO getter and setters methods here
}
In your screen, you can create an instance of result and wait for it:
WSResult result = new WSResult();
//Start thread here
new Thread(new LoadCustomersTask(result)).start();
//This is old school thread sync.
synchronized(result){
while(!result.isCompleted()){
result.wait();
}
}
//Here the thread has returned, and we can diaplay the error message if any
if(result.isSuccess()){
} else {
//Display result.getErrorMessage()
}
Then your runnable would be like this:
class LoadCustomersTask implements Runnable {
private final WSResult result;
public LoadCustomersTask(WSResult res){
result = res;
}
public void run(){
//Do the WS call
//If it went well
result.setSuccess(true);
result.setResult(jsonResultados);
//Else
result.setSuccess(false);
result.setErrorMessage("Your error message");
//In any case, mark as completed
result.setcompleted(true);
//And notify awaiting threads
synchronized(result){
result.notifyAll();
}
}
}
You can also do it with thread.join, but wait/notify is better because you not making the screen depend on the particular thread where the runnable runs. You can wait/notify on the result instance, as shown, or on the runnable if it is intended for a single use.
Question #4: Yes threads must not be abused, specially in JavaME where programs usually run in single core CPUs with a frecuency in the order of MHz. Try not to have more than 1-3 threads running at the same time. If you really need to, consider using a single thread for running all background tasks (a blocking queue).

c# wanting multiple ui threads but getting cross-reference errors instead

i'm still very new at c#, threads and forms. i'm writing a small data acquistion program. it has two threads: the main ui thread and a sensor polling/logging/charting thread. when the user clicks the "start-logging" button, it it continuously polls the sensors (over a virtual COM port), writes the response to a file, updates the main form with some basic polling stats (how many pollings per second). if the user has clicked a "monitor" button, it opens a charting form and the polling thread invokes a methods that that adds the sensors values to the chart.
i have a version of this program that works very well but i found that if i have multiple charts open (so that i can view multiple sensors in realtime), the chart updates become sporadic or stop and only the window with the focus updates smoothly. (the comm port is only 56kbaud so it's not like the polling is being swamped with data.)
so i got the "bright" idea to make charting threads, thinking this would provide multiple UI loops and would produce nice smooth charting on multiple chart forms. below is simplified code; e.g. here, the charting thread is started with the polling thread instead of when the user clicks the "monitor" button. it compiles, but when it runs, i get a cross-reference error at the point when the update_chart method is called.
seems i have a fundamental misunderstanding of several things about threads and control ownership. the chart was made in the "charting" thread, but when the "polling" thread invokes the update_chart method, the code shows that update_chart methods is being run by the "main_ui" thread. i'm open to any suggestions/advise that'll give me smooth charting and stats updates. thanks.
namespace WindowsFormsApplication1
{
public partial class Main_Form : Form
{
delegate void UpdateUIStatsDelegate(string update);
UpdateUIStatsDelegate update_stats_delegate;
static BackgroundWorker polling_thread = new BackgroundWorker();
static BackgroundWorker charting_thread = new BackgroundWorker();
public static Chart_Form chart_form = new Chart_Form();
public Main_Form()
{
Thread.CurrentThread.Name = "main_ui";
update_stats_delegate = new UpdateUIStatsDelegate(update_stats);
polling_thread.DoWork += polling_thread_DoWork;
charting_thread.DoWork += charting_thread_start;
}
private void start_logging_Click(object sender, EventArgs e)
{
start_polling_thread();
start_charting_thread();
}
private void start_polling_thread()
{
polling_thread.RunWorkerAsync();
}
private void polling_thread_DoWork(object sender, DoWorkEventArgs e)
{
string sensor_values;
Thread.CurrentThread.Name = "polling";
while (true)
{
sensor_values = poll_the_sensors_and_collect_the_responses();
log_to_file(sensor_values);
// BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });
chart_form.BeginInvoke(chart_form.update_chart_delegate, new object[] { sensor_values });
pps = compute_polling_performance();
BeginInvoke(update_stats_delegate, new object[] { pps.ToString("00") });
}
}
private void update_stats(string stat)
{
string tn = Thread.CurrentThread.Name;
// this says "main_ui", but i don't get a cross-reference error
pollings_per_second.Text = stat;
}
private void start_charting_thread()
{
charting_thread.RunWorkerAsync();
}
private void charting_thread_start(object sender, DoWorkEventArgs e)
{
Thread.CurrentThread.Name = "charting";
Chart_Form chart_form = new Chart_Form();
chart_form.Show();
while (charting_is_active) { }
}
}
public partial class Chart_Form : Form
{
public delegate void UpdateChartDelegate(string sensor_values);
public UpdateChartDelegate update_chart_delegate;
public Chart_Form()
{
string tn = Thread.CurrentThread.Name;
update_chart_delegate = new UpdateChartDelegate(update_chart);
this.Text = "a realtime plot of sensor values";
}
private void update_chart(string sensor_values)
{
string tn = Thread.CurrentThread.Name;
// this says "main_ui" and i get a cross reference error; set below.
int x = extract_x_value(sensor_values);
int y = extract_y_value(sensor_values);
chart1.Series[X_AXIS].Points.AddY(x); // <<--- i get a cross-reference runtime error here...
chart1.Series[Y_AXIS].Points.AddY(y);
}
}
}

How to get an exception raised from a long running background Task without having main thread to wait

I want to perform some long running operation (e.g. listening to some event raised by OS) on the background thread. Most of the times, operation will run continuously without any problem. But in certain rare conditions, OS level API sends some error code and I need to raise exception from background thread which has to be propagated to the main thread to show it to the user of my WinFrom application.
I had decided to use BackgroundWorker for this. But .NET 4.0 provides Task class of the Task Parallel Library which is a better option as per various blogs on the TPL.
In my application, I have to kick off the background task before actual form is shown. Since actual code is quite complex, I have written some sample code simulating real time problem:
public static Task task;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ThreadTest tt = new ThreadTest();
task = new Task(() => tt.PerformTask("hi"));
task.Start();
try
{
task.Wait();
}
catch (AggregateException aggregateException)
{
// Handle exception here.
}
Application.Run(new Form1());
}
In this code, I never see the main form simply because background task keeps running without exception and task.Wait() call makes the current thread waiting until background task finishes!
Can I use TPL's Task for such scenarios where main thread should not wait until background task is finished but at the same time, it should get exception details whenever exception is raised from the background task?
In above code, one of the solutions could be to move the task creation code at some later stage. But my question is more academic in this case.
Yes you can. Please see the code below.
The program code is:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task longRunningTask = new Task((state) =>
{
LongRunningWork.DoWork( cancellationTokenSource.Token);
},cancellationTokenSource.Token,TaskCreationOptions.LongRunning);
var newForm = new Form1(cancellationTokenSource);
new Thread((state) =>
{
longRunningTask.Start();
try
{
longRunningTask.Wait();
}
catch (AggregateException exception)
{
Action<Exception> showError = (ex) => MessageBox.Show(state as Form, ex.Message);
var mainForm = state as Form;
if (mainForm != null)
{
mainForm.BeginInvoke(showError, exception.InnerException);
}
}
}).Start(newForm);
Application.Run(newForm);
And the code for the long running task is:
public class LongRunningWork
{
public static void DoWork( CancellationToken cancellationToken)
{
int iterationCount = 0;
//While the
while (!cancellationToken.IsCancellationRequested &&iterationCount <5)
{
//Mimic that we do some long jobs here
Thread.Sleep(1000);
iterationCount++;
//The jobs may throw the exception on the specific condition
if (iterationCount ==5)
{
throw new InvalidOperationException("Invalid action");
}
}
//cancel the task
cancellationToken.ThrowIfCancellationRequested();
}
}
Finally, the code for the Form1 which includes a exit button, whose function is to terminate the program on clicking.
public partial class Form1 : Form
{
private CancellationTokenSource _cancellationTokenSource;
public Form1()
{
InitializeComponent();
}
public Form1(CancellationTokenSource cancellationTokenSource):this()
{
_cancellationTokenSource = cancellationTokenSource;
}
private void exitBtn_Click(object sender, EventArgs e)
{
//Cancel out the task
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
//Exit the program
Application.Exit();
}
}
Start your long running operation from the form itself rather than before the form is created. Remember that Application.Run() starts a message loop on the current thread, but that means you can use that message loop to poll your task from the Timer class.
class Form1 : Form
{
private Timer PollingTimer;
private Task BackgroundTask;
public Form1()
{
InitializeComponent();
// Begin the background task.
ThreadTest tt = new ThreadTest();
this.BackgroundTask = new Task(() => tt.PerformTask("hi"));
this.BackgroundTask.Start();
// Monitor the task's status by polling it regularly.
this.PollingTimer = new Timer();
this.PollingTimer.Interval = 1000; // In milliseconds.
this.PollingTimer.Tick += timerCallback;
this.PollingTimer.Start();
}
private timerCallback(object sender, EventArgs e)
{
if (this.BackgroundTask.IsFaulted)
{
// Exception information is in BackgroundTask.Exception.
}
}
}
If you dislike polling (which I do), you'll need to catch the exception from your task and marshall it back to your UI thread. The best way to do that is simply not catch the exception in the task itself and provide a continuation method which will only execute on error.
class Form1 : Form
{
private Task BackgroundTask;
public Form1()
{
InitializeComponent();
// Capture the UI thread context.
// (Note, it may be safer to run this in the Form.Load event than the constructor.
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
// Begin the background task.
ThreadTest tt = new ThreadTest();
this.BackgroundTask = new Task(() => tt.PerformTask("hi"))
// Schedule a continuation to be executed after the task is completed.
.ContinueWith((t,arg) =>
{
// Exception information is in t.Exception
},null, null,
// Only execute the continuation if the task throws an exception.
TaskContinuationOptions.OnlyOnFaulted,
// Execute the continuation on the UI thread we captured above.
uiContext);
this.BackgroundTask.Start();
}
}
MSDN references for Task.ContinueWith() and TaskScheduler.FromCurrentSynchronizationContext().
And, if you have the luxury of .NET 4.5 with async and await:
class Form1 : Form
{
private Task BackgroundTask;
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
ThreadTest tt = new ThreadTest();
try
{
// Move your Task creation and start logic into a method.
await tt.RunAsync();
}
catch (Exception ex)
{
// Really smart compiler writers make sure you're on the right thread
// and everything Just Works(tm).
}
}
}

Resources