Multiple tasks running same method - c#

I have a grid with multiple SQL queries, which I want users to allow to execute when they double click on a row in the grid. I want a user to be able to run multiple queries at the same time.
I have a small Form with a progress bar and timer for each query. The Form is displayed when a query is run and shows complete after the query has finished.
Below is my code, it seems that the method returning the data is blocking. I haven't worked with sync/threads/tasks before
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationTokenHook hook = new CancellationTokenHook(cts);
CancellationToken token = cts.Token;
//this.waitFormActivator.ShowWaitForm(true, true);
//this.waitFormActivator.SetWaitFormObject(hook);
Exception exception = null;
try
{
progressControl.Start();
Task<Dataset> t = Test(command.CommandText, token, saveFileDialog1.FileName);
var result = await t;
progressControl.Stop();
}
catch (Exception ex)
{
//success = false;
exception = ex;
//return null;
}
}
I would like the queries to run independently of each other, however currently the next query is waiting for the previous one to finish.

This piece of code is suppose to answer to your question
List<Task> tasks = new List<Task>();
tasks.Add(new Task(() => { action1 }));
tasks.Add(new Task(() => { action2 }));
tasks.Add(new Task(() => { action3 }));
tasks.Add(new Task(() => { action4 }));
Task.WhenAll(tasks);
In your context it's suppose to be like that :
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationTokenHook hook = new CancellationTokenHook(cts);
CancellationToken token = cts.Token;
his.waitFormActivator.ShowWaitForm(true, true);
his.waitFormActivator.SetWaitFormObject(hook);
eption exception = null;
try
{
progressControl.Start();
List<Task<Dataset>> tasks = new List<Task<DataSert>>();
Task<Dataset> t1 = Test(command.CommandText, token, saveFileDialog1.FileName);
Task<Dataset> t2 = Test(command.CommandText, token, saveFileDialog1.FileName);
Task<Dataset> t3 = Test(command.CommandText, token, saveFileDialog1.FileName);
Task<Dataset> t4 = Test(command.CommandText, token, saveFileDialog1.FileName);
tasks.Add(t1);
tasks.Add(t2);
tasks.Add(t3);
tasks.Add(t4);
var result = Task.WhenAll(tasks);
progressControl.Stop();
}
catch (Exception ex)
{
//success = false;
exception = ex;
//return null;
}
}

Related

C# Task Run Cancellation Token cleanup after cancellation

How do I do some cleanup after checking if the cancellation token has been canceled but what I've found is if I try to do anything it the below code, everything works as expected except the cancellation, as soon as await LeaveGroup(SIGNALR_NETWORK_TEST_GROUP); is called the thread ends and nothing after it happens I've tried several different calls and it doesn't matter it just ends immediately instead of waiting for me to clean things up before I return.
What am I missing here? I even found a couple of examples from Microsoft that lead me to believe I'm on the right track but it doesn't let me execute anything else after I've checked if it's been canceled.
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
https://learn.microsoft.com/en-us/dotnet/standard/threading/how-to-listen-for-cancellation-requests-by-polling
static void Main()
{
try
{
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var signalRClient = new SignalRClient();
Task.Run(async () => await signalRClient.Start(cancellationToken), cancellationToken);
Console.ReadKey();
cancellationTokenSource.Cancel();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public async Task Start(CancellationToken cancellationToken)
{
var signalRConnectionInfo = await GetConnectionInfo();
hubConnection = new HubConnectionBuilder()
.WithUrl(signalRConnectionInfo.Url, options =>
{
options.AccessTokenProvider = () => Task.FromResult(signalRConnectionInfo.AccessToken);
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string>("OnUpdate", param =>
{
Console.WriteLine(param);
});
await hubConnection.StartAsync();
if (hubConnection.State == HubConnectionState.Connected)
await JoinGroup(SIGNALR_NETWORK_TEST_GROUP);
while (hubConnection.State == HubConnectionState.Connected)
{
if (cancellationToken.IsCancellationRequested)
{
Debug.WriteLine("Cancellation Requested!");
await LeaveGroup(SIGNALR_NETWORK_TEST_GROUP);
throw new OperationCanceledException("SignalR connection cancelled!", cancellationToken);
}
}

Correct way of starting a forever-running loop with Async

I've an existing code I wrote some time ago, that works but I dislike the fact that the thread I start remains in loop.
This piece of code is a consumer on an IBMMQ code, waiting for messages to be processed.The problem I've is that with the following code
private Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
ConnectToAccessQueue();
Logger.Debug($"Accessed to the queue {queueName}");
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}",
checkingFrequency);
while (!cancellationToken.IsCancellationRequested)
{
Logger.Trace( () => "Listening on queues for new messages");
// isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions
{ WaitInterval = (int)checkingFrequency.TotalMilliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (string.Compare(mqMsg.Format, MQC.MQFMT_STRING, StringComparison.Ordinal) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
Thread.Sleep(100);
}
},cancellationToken);
}
//Calling method
try
{
string queueManagerName = configuration.GetValue<string>("IBMMQ:QUEUE_MANAGER_NAME");
// var queueManager = new MQQueueManager(queueManagerName,dictionary2);
QueueMonitor monitor = new QueueMonitor(configuration, "IMPORTER_RECEIVER_TEST");
//_subscription = monitor.Subscribe(receiver);
await monitor.StartAsync(cts.Token).ConfigureAwait(false);
}
catch (Exception e)
{
log.Error(e, "Error creating the queue monitor or it's subscription");
}
finally
{
WaitForCancel(cts);
}
The call to await monitor.StartAsync(cts.Token).ConfigureAwait(false); remains pending.
How should I modify my code, so that the call returns and in background the task continue to loop?
Thanks in advance
Here is how you can simplify your code by replacing Thread.Sleep with Task.Delay:
private async Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
while (true)
{
// Process mqQueue here
await Task.Delay(100, cancellationToken);
}
}
Task.Delay has the advantage that accepts a CancellationToken, so in case of cancellation the loop will exit immediately. This could be important if the pooling of the MQ was lazier (for example every 5 seconds).
private static Task _runningTask;
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
_runningTask = ExecuteQueuePolling(cts.Token);
WaitForCancel(cts);
}
private static void WaitForCancel(CancellationTokenSource cts)
{
var spinner = new SpinWait();
while (!cts.IsCancellationRequested
&& _runningTask.Status == TaskStatus.Running) spinner.SpinOnce();
}
private static Task ExecuteQueuePolling(CancellationToken cancellationToken)
{
var t = new Task(() =>
{
while (!cancellationToken.IsCancellationRequested)
; // your code
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException();
}, cancellationToken, TaskCreationOptions.LongRunning);
t.Start();
return t;
}

Stop execution if one of the tasks is faulted

I have a question on TPL tasks.
I have some tasks that are a "Show Stoppers", once one of them is faulted i dont want the method to continue running but give an exception and exit.
I tried using TaskContinuationOptions, something like this:
var res = Task.Factory.ContinueWhenAny(
new[] { task1, task2, task3},
task =>
{
throw task.Exception.Flatten();
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
this.taskScheduler);
var res1 = Task.Factory.ContinueWhenAll(
new[] { task1, task2, task3},
tasks =>
{
// DO SOME CODE
},
CancellationToken.None,
TaskContinuationOptions.NotOnFaulted,
this.taskScheduler);
return Task.WhenAny(res, res1).Unwrap();
But unfortunately there is a limitation filtering on a TaskContinuationOptions when continuing on more that a one task.
What is the solution to this?
You can implement a loop which checks if the tasks are faulted as they finish. If one of them faults, you could throw and exit the method:
List<Task> tasks = new List<Task> {task1, task2, task3}; // Show stopping tasks.
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
tasks.Remove(finishedTask);
if (finishedTask.Status == TaskStatus.Faulted)
{
// Throw and exit the method.
}
}
// Continuation code goes here.
Note this will not cancel the other ongoing tasks. You could implement a cancelling mechanism if needed using CancellationToken and explicitly cancel the remaining tasks. Your tasks will need to monitor the cancellation token to see if there was a request for cancellation, either by looking at CancellationToken.IsCancellationRequested property or by using the CancellationToken.ThrowIfCancellationRequested method:
var cts = new CancellationTokenSource();
// Generate some tasks for this example.
var task1 = Task.Run(async () => await Task.Delay(1000, cts.Token), cts.Token);
var task2 = Task.Run(async () => await Task.Delay(2000, cts.Token), cts.Token);
var task3 = Task.Run(async () => await Task.Delay(3000, cts.Token), cts.Token);
List<Task> tasks = new List<Task> {task1, task2, task3};
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
tasks.Remove(finishedTask);
if (finishedTask.Status == TaskStatus.Faulted)
{
cts.Cancel();
// Throw and exit the method.
}
}
You can use a CancellationTokenSource and run all your tasks with this cancellation token , so if the token is cancelled then rest of tasks will be cancelled as well
var cs = new CancellationTokenSource();
var options = new ParallelOptions { CancellationToken = cs.Token };
var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
try
{
Parallel.ForEach(array, options, (index) =>
{
CallCustomMethod(cs, index);
});
}
catch (OperationCanceledException ex)
{
}
void CallCustomMethod(CancellationTokenSource cs, int index)
{
try
{
if (cs.IsCancellationRequested)
{
return;
}
if (index == 4)
{
throw new Exception("Cancel");
}
Console.WriteLine(index);
}
catch
{
cs.Cancel();
}
}

C# task factory timeout

I have to execute a long process operation in a thread and continue by returning the result to a function. Here is my code :
Task<ProductEventArgs>.Factory.StartNew(() =>
{
try
{
// long operation which return new ProductEventArgs with a list of product
}
catch (Exception e)
{
return new ProductEventArgs() { E = e };
}
}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
The problem is actually I don't have a timeout. I want to put a timer in order to return something like this :
new ProductEventArgs() { E = new Exception("timeout") };
if the timeout is reached.
Can't use await/async.
Thanks a lot !
You should use CancellationTokens:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var token = cts.Token;
Task<ProductEventArgs>.Factory.StartNew(() =>
{
try
{
// occasionally, execute this line:
token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
return new ProductEventArgs() { E = new Exception("timeout") };
}
catch (Exception e)
{
return new ProductEventArgs() { E = e };
}
}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
This code does what you have expressed here:
var timeout = TimeSpan.FromSeconds(5);
var actualTask = new Task<ProductEventArgs>(() =>
{
var longRunningTask = new Task<ProductEventArgs>(() =>
{
try
{
Thread.Sleep(TimeSpan.FromSeconds(10)); // simulates the long running computation
return new ProductEventArgs();
}
catch (Exception e)
{
return new ProductEventArgs() { E = e };
}
}, TaskCreationOptions.LongRunning);
longRunningTask.Start();
if (longRunningTask.Wait(timeout)) return longRunningTask.Result;
return new ProductEventArgs() { E = new Exception("timed out") };
});
actualTask.Start();
actualTask.Wait();
Console.WriteLine("{0}", actualTask.Result.E); // handling E
As you see longRunningTask is created with TaskCreationOptions.LongRunning option. That way it will have a dedicated Thread for it's execution and does not interfere with normal behavior of ThreadPool by occupying a thread from there for too long - which will be needed for other thing like i.e. UI. That's important for long running tasks.
Note: You could then handle actualTask with ContinueWith but I wanted to express the essence here.
You may use returned task object from StartNew method and then use Wait method to determine timeout.
Task<ProductEventArgs> task = Task<ProductEventArgs>.Factory.StartNew(() => {...});
if (!Task.Wait(new TimeSpan(0,0,1,0)) // wait for 1 minute
{
// throw exception or something else if timeout
}
You can run a Task.Delay(timeout) task in parallel and check what task was first to complete (Task.WhenAny() is very handy in this case):
public void FetchProduct(TimeSpan timeout)
{
var fetchTask = Task<ProductEventArgs>.Factory.StartNew(
() =>
{
try
{
// long operation which return new ProductEventArgs with a list of product
}
catch(Exception e)
{
return new ProductEventArgs() { E = e };
}
});
Task<ProductEventArgs> resultTask;
if(timeout != Timeout.InfiniteTimeSpan)
{
var timeoutTask = Task.Delay(timeout);
resultTask = Task.WhenAny(resultTask, timeoutTask).ContinueWith<ProductEventArgs>(
t =>
{
// completed task is the result of WhenAny
if(t.Result == fetchTask)
{
return fetchTask.Result;
}
else
{
return new ProductEventArgs() { E = new TimeoutException() };
}
});
}
else
{
resultTask = fetchTask;
}
resultTask.ContinueWith(x => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
}
Note that this solution doesn't have any cancellation logic, and your long running task will be still running even if it times out.
Just start another task within the main task (surrogate):
Task.Factory.StartNew(() =>
{
// returns a string result
var tsk = new Task<string>(() => { return VeryImportantThingsToDo(); });
try
{
tsk.Start();
if (!tsk.Wait(5000))
throw new TimeoutException();
return tsk.Result;
}
catch (TimeoutException)
{
// Jabba Dabba Doooooooohhhhhh
}
return "<unknown>";
}).ContinueWith((o) => string result = o.Result));

Stop Multiple Tasks at once

I am starting multiple tasks with a parallel foreach loop.
Now I want on click of a stop button all task to be stopped. How can I do this?
Here is my code:
tasks2 = new List<Task>();
Parallel.ForEach<RssToProcess>(RssFeeds, rssFeed =>
tasks2.Add(Task.Factory.StartNew(() =>
{
string urlss = rssFeed.RssUrl;
nourl += urlss + System.Environment.NewLine;
RssReader rs = new RssReader();
rs.FeedsourceLoaded += new EventHandler(rs_FeedsourceLoaded);
rs.ItemAdded += new EventHandler(rs_ItemAdded);
rs.AllItemAdded += new EventHandler(rs_AllItemAdded);
rs.RssReaderrssitemsCountgeta += new EventHandler(rs_RssReaderrssitemsCountgeta);
rs.RdfMode = true;
RssFeed f = rs.Retrieve(rssFeed.RssUrl);
})));
You should probably be passing a CancellationToken:
http://msdn.microsoft.com/en-us/library/dd537607.aspx
Create a CancellationToken:
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
And pass it to your tasks:
Task.Factory.StartNew(() =>
{
... Do Work ...
}, token);
When you want to cancel, call
tokenSource.Cancel();
However, note that task cancellation is cooperative.
Within your tasks, you will need to use
token.ThrowIfCancellationRequested(); // To Abort immediately.
... OR ...
if (token.IsCancellationRequested)
{
// Exit your task manually.
}

Categories