How to use unity "JobSystem" Without main thread , And not freeze? - c#

I want to use that load some in backgound , But Jobsystem probably use the main thread , So how to do use jobsystem Wituhot Freezes , is it impossible ??
Or I just use C# Thread?? Dont Use JobSystem ??
struct SleepJob : IJobParallelFor
{
public void Execute(int index)
{
Debug.LogFormat("[SleepJob.Execute] Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1);
}
}
struct SleepJob2 : IJobParallelFor
{
public void Execute(int index)
{
Debug.LogFormat("[SleepJob2.Execute] Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1);
}
}
[ContextMenu("JobSleep")]
public void JobSleep()
{
Debug.LogFormat("[JobSleep.Execute] Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
SleepJob job = new SleepJob() { };
SleepJob2 job2 = new SleepJob2() { };
JobHandle jh = job.Schedule(100, 64);
JobHandle jh2 = job2.Schedule(100, 64, jh);
JobHandle.ScheduleBatchedJobs();
jh2.Complete(); // freezes
Debug.LogFormat("[JobSleep.Execute] jh.Complete();");
}

Note that Complete makes your execution freeze because:
The JobSystem automatically prioritizes the job and any of its dependencies to run first in the queue, then attempts to execute the job itself on the thread which calls the Complete function.
I think this is not the method you want to use.
Rather try waiting for the Job to complete in a Coroutine like e.g.
[ContextMenu("JobSleep")]
public void JobSleep()
{
Debug.LogFormat("[JobSleep.Execute] Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
SleepJob job = new SleepJob() { };
SleepJob2 job2 = new SleepJob2() { };
JobHandle jh = job.Schedule(100, 64);
JobHandle jh2 = job2.Schedule(100, 64, jh);
JobHandle.ScheduleBatchedJobs();
StartCoroutine(WaitFor(jh2));
}
IEnumerator WaiFor(JobHandle job)
{
yield return new WaitUntil(() => job.IsComplete);
Debug.LogFormat("[JobSleep.Execute] job IsComplete");
}
Unfortunately you didn't add the code you actually want to be executed in background.
In general maybe simply using a Thread or async might already solve your problem.
e.g. with a Thread
// for sending back responses to the main thread
private ConcurrentQueue<Action> callbacks = new ConcurrentQueue<Action>;
// work the callbacks in the main thread
private void Update()
{
while(callbacks.Count > 0)
{
Action callback;
if(callbacks.TryDequeue(out callback)) callback?.Invoke();
}
}
// optional callback on success
public void StartBackgroundTask(Action onSuccess = null)
{
var thread = new Thread(new ParameterizedThreadStart(TheTaskThatTakesAWhile);
// pass in parameters here
thread.Start(onSuccess);
}
// Will be running in a background thread
private void TheTaskThatTakesAWhile(Action onSuccess)
{
// hand this back to the main thread
callbacks.Enqueue(() => Debug.Log("Long task started ..."));
// TODO whatever takes so long
// Note btw that sleep is in milliseconds!
Thread.Sleep(1000);
hand this back to the mainthread
callbacks.Enqueue(() =>
{
Debug.Log("Long task started ..."));
onSuccess?.Invoke();
}
}
e.g. using async (which internally also runs in a Thread)
private async Task TheTaskThatTakesAWhile()
{
// do whatever takes long here
}
// usually you should avoid having async void
// but when including an Action as callback this is fine
public async void StartBackgroundTask(Action onSuccess = null)
{
await TheTaskThatTakesAWhile();
onSuccess?.Invoke();
}
Both you could call like e.g.
StartBackgroundTask(() => {
// what should happen when done?
Debug.Log("I'm done!");
});

Make JobHandle jh2 global variable. Check JobHandle.isComplete in Update method or in Coroutine

Related

Replace 'BackgroundWorker' With 'Thread '

I want to replace BackgroundWorker in my winform application with a Thread.
The goal is do the the jobs in a new thread other than UI-thread & prevent program hang during run.
So i did this :
private void radBtn_start_Click(object sender, EventArgs e)
{
try
{
string thread_name = "trd_" + rnd.Next(99000, 10000000).ToString();
Thread thread = new Thread(new ThreadStart(Thread_Method));
thread.Name = thread_name;
thread.Start();
}
catch (System.Exception ex)
{
MessageBox.Show("Error in radBtn_start_Click() Is : " + ex.ToString());
}
}
public void Thread_Method()
{
...Some Jobs
Thread.Sleep(20000);
...Some Jobs After Delay
Thread.Sleep(20000);
...Some Jobs After Delay
this.Invoke(new MethodInvoker(delegate
{
radTextBoxControl1.Text += DateTime.Now.ToString() + " : We are at end of search( " + radDropDownList1.SelectedItem.Tag + " ) = -1" + Environment.NewLine;
}));
}
But after running these codes UI hangs during sleep.
What is the correct codes for my purpose?
You don't have to create a new Thread, your process already has a pool of threads anxiously waiting to do something for you
Usually the threads in the thread pool are used when you use async-await. However, you can also use them for heavy calculations
My advice is to make your thread_method async. This has the advantage, that whenever your thread_method has to wait idly for another process to finish, like writing data to a file, fetching items from a database, or reading information from the internet, the thread is available for the thread pool to do other tasks.
If you are not familiar with async-await: this interview with Eric Lippert really helped me to understand what happens when you use async-await. Search somewhere in the middle for async-await.
One of the nice things about async-await, is that the executing thread has the same "context" as the UI-thread, so this thread can access UI-elements. No need to check for InvokeRequired or to call Invoke.
To make your ThreadMethod async:
declare it async
instead of TResults return Task<TResult>; instead of void return Task
only exception: async event handlers return void
whenever you call other methods that have an async version, call this async version, start awaiting when you need the results of the async task.
public async Task FetchCustomerAddress(int customerId)
{
// fetch the customer address from the database:
using (var dbContext = new OrderDbContext(...))
{
return await dbContext.Customers
.Where(customer => customer.Id == customerId)
.Select(customer => new Address
{
Name = customer.Name,
Street = customer.Street,
... // etc
})
.FirstOrDefaultAsync();
}
}
public async Task CreateCustomerOrder(
int customerId, IEnumerable orderLines)
{
// start reading the customer Address
var taskReadCustomerAddress = this.FetchCustomerAddress(customerId);
// meanwhile create the order
CustomerOrder order = new CustomerOrder();
foreach (var orderLine in orderLines)
{
order.OrderLines.Add(orderLine);
}
order.CalculateTotal();
// now you need the address of the customer: await:
Address customerAddress = await taskReadCustomerAddress;
order.Address = customerAddress;
return order;
}
Sometimes you don't have to wait idly for another process to finish, but you need to do some heavy calculations, and still keep your UI-thread responsive. In older applications you would use the BackgroundWorker for this, in newer applications you use Task.StartNew
For instance, you have a button, and a menu item that both will start some heavy calculations. Just like when using the backgroundworker you want to show some progress. While doing the calculations, both the menu item and the button need to be disable.
public async Task PrintCustomerOrdersAsync(
ICollection<CustomerOrderInformation> customerOrders)
{
// while creating the customer orders: disable the button and the menu items
this.buttonPrintOrders.Enabled = false;
this.menuItemCreateOrderLines.Enabled = false;
// show the progress bar
this.ProgressBarCalculating.MinValue = 0;
this.ProgressBarCalculating.MaxValue = customers.Count;
this.ProgressBarCalculating.Value = 0;
this.ProgressBarCalculating.Visible = true;
List<Task<PrintJob>> printJobs = new List<Task<PrintJob>>();
foreach (CustomerOrderInformation orderInformation in customerOrders)
{
// instead of BackGroundworker raise event, you can access the UI items yourself
CustomerOrder order = this.CreateCustomerOrder(orderInformation.CustomerId,
orderInformation.OrderLines);
this.ProgressBarCalculating.Value +=1;
// print the Order, do not await until printing finished, create next order
printJobs.Add(this.Print(order));
}
// all orders created and sent to the printer. await until all print jobs complete:
await Task.WhenAll(printJobs);
// cleanup:
this.buttonPrintOrders.Enabled = true;
this.menuItemCreateOrderLines.Enabled = true;
this.ProgressBarCalculating.Visible = false;
}
By the way: in a proper design, you would separate the enabling / disabling the items from the actual processing:
public async Task PrintCustomerOrdersAsync(ICollection<CustomerOrderInformation> customerOrders)
{
this.ShowBusyPrintingOrders(customerOrders.Count);
await this.PrintOrdersAsync(customerOrders);
this.HideBusyPrintingOrders();
}
Now to start printing the orders when a button is pressed, there are two possibilities:
If the process is mostly waiting for others: async event handler
If there are really heavy calculations (longer than a second?): start a task that does the calculations
No heavy calculations:
// async event handler has void return value!
private async void ButtonPrintOrdersClickedAsync(object sender, ...)
{
var orderInformations = this.GetOrderInformations();
await PrintCustomerOrdersAsync(orderInformations);
}
Because I don't have anything other useful to do, I await immediately
Heavy calculations: start a separate task:
private async Task ButtonCalculateClickedAsync(object sender, ...)
{
var calculationTask = Task.Run(() => this.DoHeavyCalculations(this.textBox1.Text);
// because you didn't await, you are free to do something else,
// for instance show progress:
while (!calculationTask.Complete)
{
// await one second; UI is responsive!
await Task.Delay(TimeSpan.FromSeconds(1));
this.ProgressBar.Value += 1;
}
}
Be aware: using these methods, you can't stop the process. So you are in trouble if the operator wants to close the application while you are still printing.
Just like your background thread, every method that supports cancellation should regularly check if cancellation is requested. The advantage is, that this checking is also done in the .NET methods that support cancellation, like reading database information, writing a file, etc. The backgroundWorker couldn't cancel writing to a file.
For this we have the CancellationTokenSource
private CancellationTokenSource cancellationTokenSource;
private Task taskPrintOrders;
public async Task PrintCustomerOrdersAsync(ICollection<CustomerOrderInformation> customerOrders)
{
this.ShowBusyPrintingOrders(customerOrders.Count);
using (this.cancellactionTokenSource = new CancellationTokenSource())
{
taskPrintOrders = this.PrintOrdersAsync(customerOrders, this.cancellationTokenSource.Token);
await taskPrintOrders;
this.HideBusyPrintingOrders();
}
private void CancelPrinting()
{
this.cancellationTokenSource?.Cancel();
}
If you want to cancel and wait until finished, for instance when closing the form:
private bool TaskStillRunning => this.TaskPrinting != null && !this.TaskPrinting.Complete;
private async void OnFormClosing(object sender, ...)
{
if (this.TaskStillRunning)
{
bool canClose = this.AskIfCanClose();
if (!canClose)
eventArgs.Cancel = true;
else
{
// continue closing: stop the task, and wait until stopped
this.CancelPrinting();
await this.taskPrintOrders;
}
}
}
This will work in separate thread without hanging your UI.
Use new Thread
new Thread(delegate()
{
Thread_Method();
}).Start();
or Task.run
Task.Run(() =>
{
Thread_Method();
});

Change execution thread of the current method

With tasks it is possible to do something like this:
public async Task SomeMethod()
{
// [A] Here I am in the caller thread
await OtherMethod().ConfigureAwait( false );
// [B] Here I am in some other thread
}
private async Task OtherMethod()
{
// Something here
}
Where in points [A] and [B] you can be in different threads. Is it possible to do something similar withous async and await keywords with choosing the thread, it will swtitch to? Like so:
public void SomeMethod()
{
// [A] Here I am in the caller thread
ChangeThread();
// [B] Here I am in some other thread
}
private void ChangeThread()
{
Thread thread = new Thread(???);
// ???
}
I know this is possible with delegates, but is is possible to switch thread inside of a method, with possibly changing current thread back when method ends? If not, is it possible to craft something using async/await that can change thread, but I am in control of to which thread it will switch to (like UI thread using Control.Invoke)?
What I always do in cases where I need to change execution context and then go back to original context is :
public async void RunWorkerAsync()
{
var result = await RetriveDataAsync();
}
public Task<Object<TItem>> RetriveResultsAsync()
{
var tokenSource = new CancellationTokenSource();
var ct = tokenSource.Token;
var source = new TaskCompletionSource<Object<TItem>>();
var task = Task.Run(() =>
{
// [B] Here I am in some other thread
while (!ConditionToStop)
{
if (ct.IsCancellationRequested)
{
tokenSource.Cancel();
ct.ThrowIfCancellationRequested();
}
}
}, ct).ContinueWith(taskCont =>
{
if (resultedData != null)
{
source.SetResult(resultedData);
}
}, ct);
bool taskCompleted = task.Wait(2000, ct);
if (!taskCompleted)
{
tokenSource.Cancel();
}
return source.Task;
}
In case you want to execute all in one task with no results just pass the data and remove the taskCompleted part and rely only in the Condition to stop. Al your code will run on another thread and after in is completed execution will return to your calling thread. If something simple with no returns is what you need just use
Task.Run(Action() => ExecuteSomething);
within the method.

How to make a pause in a procedure and then return value after it?

I'm working on a C# project, want to make a small pause about 2 seconds inside a procedure.
Actually I have tried to use Invoke, but as you know, we can't use it inside a class this kind of procedure.
Here is my code for more details:
public class GenerateFile
{
public CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
string script = string.Format(" DECLARE #RC INT " +
" DECLARE #Daftar_No INT = '{0}' " +
" DECLARE #hokm_type_code INT = 100 " +
" DECLARE #Channelno INT = '{1}' " +
" DECLARE #Id_No BIGINT = '{2}' " +
" EXEC #rc = [dbo].[Hokm_with_type] #Daftar_No, #hokm_type_code, #Channelno, #Id_No ",
Daftar_No,
Channelno,
NationalCode);
try
{
IEnumerable<string> commandStrings = Regex.Split(script, #"^\s*GO\s*$",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connect();
foreach (string commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, Connection))
{
command.ExecuteNonQuery();
}
}
}
DisConnect();
string FaxFilePath = InternalConstant.FaxFilePath + "\\" + string.Format("Lhokm{0}.tif", Channelno);
// I want to make a pause in here without locking UI
if (File.Exists(FaxFilePath))
return CSPFEnumration.ProcedureResult.Success;
else
return CSPFEnumration.ProcedureResult.Error;
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
return CSPFEnumration.ProcedureResult.Error;
}
}
}
I have tried await too, but I cant return a proper value. because in this procedure if I use await, the value will return before finishing await.
Edit:
And also I dont want to use Thread.Sleep because it will lock UI.
Thanks for any helping.
Use async await feature :
Mark your method as async .
Add Task.Delay(2000) as the waited task.
public async CSPFEnumration.ProcedureResult GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
-----
// I want to make a pause in here without locking UI
await Task.Delay(2000);
-----
}
Asking for downvotes:
DoEvents
Warning: Total, Complete and Inexcusably Flagrant Barnyard Programming:
// before call (disable the UI element that called this so it can't re-enter)
DateTime st = DateTime.Now();
while(DateTime.Now.Subtract(st).TotalSeconds<3)
System.Windows.Forms.DoEvents();
// after call (re-enable UI element)
This will appear to work. No responsibility if people point and laugh.
Hey, you asked!
You can look around Task.Delay() it will not block current thread and continue execution after number of milliseconds.
Exmaple usage from msdn:
Stopwatch sw = Stopwatch.StartNew();
var delay = Task.Delay(1000).ContinueWith(_ =>
{ sw.Stop();
return sw.ElapsedMilliseconds; } );
Console.WriteLine("Elapsed milliseconds: {0}", delay.Result);
// The example displays output like the following:
// Elapsed milliseconds: 1013
Or maybe look around Timer class.
I can see it working with events or Tasks (if you cannot use async / await). This is how to create event. We can use separate Thread to check if file is created and fire event if it is:
public class FileGenEventArgs : EventArgs
{
public string ProcedureResult { get; set; }
}
public class GenerateFile
{
public event EventHandler<FileGenEventArgs > fileCreated;
public GenerateFile()
{
// subscribe for this event somewhere in your code.
fileCreated += GenerateFile_fileCreated;
}
void GenerateFile_fileCreated(object sender, FileGenEventArgs args)
{
// .. do something with args.ProcedureResult
}
private void FileCheck()
{
Thread.Sleep(2000); // delay
fileCreated(this, new FileGenEventArgs()
{
ProcedureResult = File.Exists(FaxFilePath) ?
CSPFEnumration.ProcedureResult.Success :
CSPFEnumration.ProcedureResult.Error
});
}
public void GenerateFaxFile(string Daftar_No, string Channelno, string NationalCode)
{
try
{
// this .Sleep() represents your sql operation so change it
Thread.Sleep(1000);
new Thread(FileCheck).Start();
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
}
}
}
Pros :
Pause that you wanted
Doesn't block the UI thread.
Event-based approach (which is proper way of dealing with this kind of problems)
Cons :
Requires to refactor your code
The most easy thing to wait while keeping the UI responsive is using async-await.
To do this, you must declare your function async, and return Task instead of void and Task<TResult> instead of TResult:
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
// do your stuff,
}
Now whenever you do something that takes some time, use the async version of the function to start the process. While this process is running, you can do other stuff. When you need the result await for the task, and you get the void if the async returns Task, or the TResult if the async returns Task<TResult>
public async Task<CSPFEnumration.ProcedureResult> GenerateFaxFile(
string Daftar_No,
string Channelno,
string NationalCode)
{
IEnumerable<string> commandStrings = Regex.Split(
script, #"^\s*GO\s*$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
Connect();
foreach (var commandString in commandStrings)
{
if (commandString.Trim() != "")
{
using (var command = new SqlCommand(commandString, Connection))
{
Task<int> task = command.ExecuteNonQueryAsync();
// while the command is being executed
// you can do other things.
// when you need the result: await
int result = await task;
// if useful: interpret result;
}
}
}
DisConnect();
... etc.
}
Every function that calls an async function should be declared async
every async function returns Task instead of void and Task<TResult> instead of TResult
There is only one exception: the event handler may return void.
Example of async event handler:
private async void OnButton1_Clicked(object sender, ...)
{
var task = GenerateFaxFile(...);
// while the fax file is generated do some other stuff
// when you need the result:
var procedureResult = await task;
Process(procedureResult);
}
Note that everything is processed by the UI thread. The only difference is that as soon as anything time consuming happens, the process doesn't have a busy wait, but processes UI input.
The above is enough to keep your UI responsive. You said you wanted to know how to wait some time. From the rest of your question I understand that you meant: how to interrupt the procedure while it is waiting for something, so the UI can do other thing. If you really need to wait some time while keeping the UI responsive, use Task.Delay(TimeSpan).
Eric Lippert (thanx Eric!) explained async-await as follows in Stackoverflow - async/await - Is this understanding correct?
Suppose for breakfast you have to toast bread and cook eggs. There are several scenarios for it:
Start toasting bread. Wait until it is finished. Start cooking eggs, wait until it is finished. Synchronous processing. While you are waiting for the bread to toast you can't do anything else.
Start toasting bread, while the bread is being toasted start cooking eggs. when the eggs are cooked wait until the bread finished toasting. This is called Asynchronous, but not concurrent. It is done by the main thread and as long as this thread does something, the main thread can't do anything else. But while it is waiting it has time to do other things (make some tea for instance)
Hire cooks to toast the bread and cook the eggs. Wait until both are finished. Asynchronous and concurrent: the work is done by different threads. This is the most expensive because you have to start new threads.
Finally a note about your exception handling
Do you notice that if an exception occurs you don't disconnect?. The proper way to make sure that disconnect is always called is the following:
try
{
Connect();
... do other stuff
}
catch (Exception exc)
{
... process exception
}
finally
{
Disconnect();
}
The finally part is always executed, regardless of any exception being thrown or not.
You can use simple Thread Pool to archive this. However your return has to do asynchronously so it doesn't lockup the gui.
public void GenerateFaxFile(string Daftar_No, string Channelno,
string NationalCode, Action<CSPFEnumration.ProcedureResult> result)
{
ThreadPool.QueueUserWorkItem(o =>
{
string script = "your script";
try
{
// more of your script
// I want to make a pause in here without locking UI
while (true)
{
// do your check here to unpause
if (stopMe == true)
{
break;
}
Thread.Sleep(500);
}
if (File.Exists(FaxFilePath))
{
result(CSPFEnumration.ProcedureResult.Success);
return;
}
else
{
result(CSPFEnumration.ProcedureResult.Error);
}
}
catch (Exception ex)
{
InternalDatabase.GetInstance.InsertToPensionOrganizationException(ex);
result(CSPFEnumration.ProcedureResult.Error);
return;
}
});
}
public void HowToUseMe()
{
GenerateFaxFile("", "", "", result => {
if (result == CSPFEnumration.ProcedureResult.Error)
{
// no good
}
else
{
// bonus time
}
});
}
You should use the old good background thread (see answer written by FabJan) or you can use async and await with synchronization context:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void buttonStart_Click(object sender, EventArgs e)
{
await progressBar1.DoProgress(2000);
Trace.WriteLine("Done");
MessageBox.Show("Done");
}
private void buttonMoveButton1_Click(object sender, EventArgs e)
{
//to prove UI click several times buttonMove while the task is ruunning
buttonStart.Top += 10;
}
}
public static class WaitExtensions
{
public static async Task DoProgress(this ProgressBar progressBar, int sleepTimeMiliseconds)
{
int sleepInterval = 50;
int progressSteps = sleepTimeMiliseconds / sleepInterval; //every 50ms feedback
progressBar.Maximum = progressSteps;
SynchronizationContext synchronizationContext = SynchronizationContext.Current;
await Task.Run(() =>
{
synchronizationContext.OperationStarted();
for (int i = 0; i <= progressSteps; i++)
{
Thread.Sleep(sleepInterval);
synchronizationContext.Post(new SendOrPostCallback(o =>
{
Trace.WriteLine((int)o + "%");
progressBar.Value = (int)o;
}), i);
}
synchronizationContext.OperationCompleted();
});
}
}
It could appear that MessageBox done Shows before the ProgressBar is on its Maximum. I blame for this magic animation of progressBar in Windows 8. Please correct me if I am wrong.

Cancel Long Running task [duplicate]

In a thread, I create some System.Threading.Task and start each task.
When I do a .Abort() to kill the thread, the tasks are not aborted.
How can I transmit the .Abort() to my tasks ?
You can't. Tasks use background threads from the thread pool. Also canceling threads using the Abort method is not recommended. You may take a look at the following blog post which explains a proper way of canceling tasks using cancellation tokens. Here's an example:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
Like this post suggests, this can be done in the following way:
int Foo(CancellationToken token)
{
Thread t = Thread.CurrentThread;
using (token.Register(t.Abort))
{
// compute-bound work here
}
}
Although it works, it's not recommended to use such approach. If you can control the code that executes in task, you'd better go with proper handling of cancellation.
Aborting a Task is easily possible if you capture the thread in which the task is running in. Here is an example code to demonstrate this:
void Main()
{
Thread thread = null;
Task t = Task.Run(() =>
{
//Capture the thread
thread = Thread.CurrentThread;
//Simulate work (usually from 3rd party code)
Thread.Sleep(1000);
//If you comment out thread.Abort(), then this will be displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.Sleep(10);
//Cancel the task by aborting the thread
thread.Abort();
}
I used Task.Run() to show the most common use-case for this - using the comfort of Tasks with old single-threaded code, which does not use the CancellationTokenSource class to determine if it should be canceled or not.
This sort of thing is one of the logistical reasons why Abort is deprecated. First and foremost, do not use Thread.Abort() to cancel or stop a thread if at all possible. Abort() should only be used to forcefully kill a thread that is not responding to more peaceful requests to stop in a timely fashion.
That being said, you need to provide a shared cancellation indicator that one thread sets and waits while the other thread periodically checks and gracefully exits. .NET 4 includes a structure designed specifically for this purpose, the CancellationToken.
I use a mixed approach to cancel a task.
Firstly, I'm trying to Cancel it politely with using the Cancellation.
If it's still running (e.g. due to a developer's mistake), then misbehave and kill it using an old-school Abort method.
Checkout an example below:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (runningTaskThread != null)
{
try
{
runningTaskThread.Join(TimeSpan.FromSeconds(1));
}
catch (Exception ex)
{
runningTaskThread.Abort();
}
}
}
To answer Prerak K's question about how to use CancellationTokens when not using an anonymous method in Task.Factory.StartNew(), you pass the CancellationToken as a parameter into the method you're starting with StartNew(), as shown in the MSDN example here.
e.g.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
You should not try to do this directly. Design your tasks to work with a CancellationToken, and cancel them this way.
In addition, I would recommend changing your main thread to function via a CancellationToken as well. Calling Thread.Abort() is a bad idea - it can lead to various problems that are very difficult to diagnose. Instead, that thread can use the same Cancellation that your tasks use - and the same CancellationTokenSource can be used to trigger the cancellation of all of your tasks and your main thread.
This will lead to a far simpler, and safer, design.
Tasks have first class support for cancellation via cancellation tokens. Create your tasks with cancellation tokens, and cancel the tasks via these explicitly.
You can use a CancellationToken to control whether the task gets cancelled. Are you talking about aborting it before it's started ("nevermind, I already did this"), or actually interrupting it in middle? If the former, the CancellationToken can be helpful; if the latter, you will probably need to implement your own "bail out" mechanism and check at appropriate points in the task execution whether you should fail fast (you can still use the CancellationToken to help you, but it's a little more manual).
MSDN has an article about cancelling Tasks:
http://msdn.microsoft.com/en-us/library/dd997396.aspx
Task are being executed on the ThreadPool (at least, if you are using the default factory), so aborting the thread cannot affect the tasks. For aborting tasks, see Task Cancellation on msdn.
I tried CancellationTokenSource but i can't do this. And i did do this with my own way. And it works.
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<string> ConnectTab()
{
string messageText = "";
for (int count = 1; count < 20; count++)
{
if (count == 1)
{
//Do stuff.
}
try
{
//Do stuff.
}
catch (Exception ex)
{
//Do stuff.
}
if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
{
return messageText = "Task stopped."; //4-) And so return and exit the code and task.
}
if (Connected)
{
//Do stuff.
}
if (count == 19)
{
//Do stuff.
}
}
return messageText;
}
}
}
And another class of the calling the method:
namespace Blokick.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
InitializeComponent();
signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.
if (signalR.ChatHubProxy != null)
{
signalR.Disconnect();
}
LoadSignalRMessage();
}
}
}
You can abort a task like a thread if you can cause the task to be created on its own thread and call Abort on its Thread object. By default, a task runs on a thread pool thread or the calling thread - neither of which you typically want to abort.
To ensure the task gets its own thread, create a custom scheduler derived from TaskScheduler. In your implementation of QueueTask, create a new thread and use it to execute the task. Later, you can abort the thread, which will cause the task to complete in a faulted state with a ThreadAbortException.
Use this task scheduler:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Start your task like this:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Later, you can abort with:
scheduler.TaskThread.Abort();
Note that the caveat about aborting a thread still applies:
The Thread.Abort method should be used with caution. Particularly when you call it to abort a thread other than the current thread, you do not know what code has executed or failed to execute when the ThreadAbortException is thrown, nor can you be certain of the state of your application or any application and user state that it is responsible for preserving. For example, calling Thread.Abort may prevent static constructors from executing or prevent the release of unmanaged resources.
You can use this class..:
It works for all typs of returned Values..
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CarNUChargeTester
{
public class TimeOutTaskRunner<T>
{
private Func<T> func;
private int sec;
private T result;
public TimeOutTaskRunner(Func<T> func, int sec)
{
this.func = func;
this.sec = sec;
}
public bool run()
{
var scheduler = new SingleThreadTaskScheduler();
Task<T> task = Task<T>.Factory.StartNew(func, (new CancellationTokenSource()).Token, TaskCreationOptions.LongRunning, scheduler);
if (!task.Wait(TimeSpan.FromSeconds(sec)))
{
scheduler.TaskThread.Abort();
return false;
}
result = task.Result;
return true;
}
public T getResult() { return result; }
}
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException();
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException();
}
}
To use it you can write:
TimeOutTaskRunner<string> tr = new TimeOutTaskRunner<string>(f, 10); // 10 sec to run f
if (!tr.run())
errorMsg("TimeOut"); !! My func
tr.getResult() // get the results if it done without timeout..

Using ThreadPool.QueueUserWorkItem - thread unexpectedly exits

I have the following method:
public void PutFile(string ID, Stream content)
{
try
{
ThreadPool.QueueUserWorkItem(o => putFileWorker(ID, content));
}
catch (Exception ex)
{
OnPutFileError(this, new ExceptionEventArgs { Exception = ex });
}
}
The putFileWorker method looks like this:
private void putFileWorker(string ID, Stream content)
{
//Get bucket name:
var bucketName = getBucketName(ID)
.ToLower();
//get file key
var fileKey = getFileKey(ID);
try
{
//if the bucket doesn't exist, create it
if (!Amazon.S3.Util.AmazonS3Util.DoesS3BucketExist(bucketName, s3client))
s3client.PutBucket(new PutBucketRequest { BucketName = bucketName, BucketRegion = S3Region.EU });
PutObjectRequest request = new PutObjectRequest();
request.WithBucketName(bucketName)
.WithKey(fileKey)
.WithInputStream(content);
S3Response response = s3client.PutObject(request);
var xx = response.Headers;
OnPutFileCompleted(this, new ValueEventArgs { Value = ID });
}
catch (Exception e)
{
OnPutFileError(this, new ExceptionEventArgs { Exception = e });
}
}
I've created a little console app to test this.
I wire up event handlers for the OnPutFileError and OnPutFileCompleted events.
If I call my PutFile method, and step into this, it gets to the "//if the bucket doesn't exist, create it" line, then exits. No exception, no errors, nothing.
It doesn't complete (i've set breakpoints on my event handlers too) - it just exits.
If I run the same method without the ThreadPool.QueueUserWorkItem then it runs fine...
Am I missing something?
ThreadPool threads are background threads (see the link). They will not keep your application running if the main thread exits.
Typically, in WinForms apps, this is not a problem, because the main UI thread calls Application.Run and starts processing events. For your console app, if your Main method doesn't wait for the work item to complete somehow, the main thread will queue the work item and then exit.
You could create a background thread yourself and set its IsBackground property to false. Or you could create a thread and call Thread.Join to wait for it to finish.
-- EDIT --
As suggested in the comments below, you could also use a ManualResetEvent, or even a custom synchronization class as suggested by Linik. The goal is to block the main thread until the the background threads have completed.
To use a ManualResetEvent, create it in your main thread and pass it in as an argument. (I'll assign it to a static variable here just for brevity.)
ManualResetEvent s_WaitEvent;
ManualResetEvent s_WaitEvent = new ManualResetEvent(false); // non-signaled
// queue work item here
s_WaitEvent.WaitOne();
At the end of your worker thread, signal the event:
s_WaitEvent.Set();
Link's CountDownLatch is nice if you have many threads that must process before you can exit. You can also use separate ManualResetEvents for each thread and wait for them all to complete using WaitHandle.WaitAll(WaitHandle[]). (ManualResetEvent inherits from WaitHandle.)
Put a Console.ReadLine() in your Main thread to block it while you test your worker thread. This will keep main from exiting. Just hit enter when you're done.
Use a CountDownLatch to force the main to wait for all of the threads that you have queued up:
public class CountDownLatch
{
private int m_remain;
private EventWaitHandle m_event;
public CountDownLatch (int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException();
m_remain = count;
m_event = new ManualResetEvent(false);
if (m_remain == 0)
{
m_event.Set();
}
}
public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait()
{
m_event.WaitOne();
}
}
In Main:
static void Main(string[] args)
{
CountDownLatch latch = new CountDownLatch(numFiles);
//
// ...
//
putFileWorker("blah", streamContent);
//
// ...
//
// waits for all of the threads to signal
latch.Wait();
}
In the worker method:
private void putFileWorker(string ID, Stream content)
{
try
{
//Get bucket name:
var bucketName = getBucketName(ID)
.ToLower();
//get file key
var fileKey = getFileKey(ID);
try
{
//if the bucket doesn't exist, create it
if (!Amazon.S3.Util.AmazonS3Util.DoesS3BucketExist(bucketName, s3client))
s3client.PutBucket(new PutBucketRequest { BucketName = bucketName, BucketRegion = S3Region.EU });
//
// ...
//
}
catch (Exception e)
{
OnPutFileError(this, new ExceptionEventArgs { Exception = e });
}
}
finally
{
latch.Signal();
}
}

Categories

Resources