This question already has an answer here:
TaskScheduler with async sequential Tasks C#
(1 answer)
Closed 5 years ago.
Hi i have textbox which has text changed event. Everytime a character is inserted in textbox the text changed event is fired. The Text changed event calls a async Task method. Below is the my event and async Task method.
public Textbox_TextChangedEvent()
{
GetStocks(texboxText);
}
public async Task GetStocks(string texboxText)
{
IsBusy = true;
await Task.Run(() => { CreateCollection(texboxText); });
IsBusy = false;
}
Question
How can i make sure GetStocks method is called synchronously one after the other.
Example
Suppose user has input Ted as input text. Then i want the the async call to be completed one after other.
i.e it should call GetStocks in following order and also complete the task in following order itself.
GetStocks(T)
GetStocks(Te)
GetStocks(Ted)
To solve problems like this, we have used an AsyncLock previous projects. The AsyncLock will wait until the previous lock was released.
The AsyncLock may seem a little bit complicated first, but i hope the provided usage examples will illustrate its behaviour.
public class AsyncLock
{
private TaskCompletionSource<object> _lastSection;
public AsyncLock()
{
_lastSection = new TaskCompletionSource<object>();
_lastSection.SetResult(null);
}
public class ReleaseLock : IDisposable
{
private readonly TaskCompletionSource<object> _tcs;
public ReleaseLock(TaskCompletionSource<object> tcs)
{
_tcs = tcs;
}
public void Dispose()
{
_tcs.SetResult(null);
}
}
/// <summary>
/// Enters and locks a critical section as soon as the currently executing task has left the section.
/// The critical section is locked until the returned <see cref="IDisposable"/> gets disposed.
/// </summary>
public Task<ReleaseLock> EnterAsync()
{
var newTcs = new TaskCompletionSource<object>();
var toAwait = Interlocked.Exchange(ref _lastSection, newTcs);
return toAwait.Task.ContinueWith((_) => new ReleaseLock(newTcs), TaskContinuationOptions.ExecuteSynchronously);
}
}
You then can use await AsyncLock.EnterAsync() to wait until any previous lock was released. In the EnterAsync we queue the next Task after the current Task using ContinueWith. This means the await AsyncLock.EnterAsync() will be executed after the previous has finished.
using (await _lock.EnterAsync())
{
// ...
}
Here is an usage example:
class Program
{
private static readonly AsyncLock _lock = new AsyncLock();
private static async Task Test(int i, Task toComplete)
{
using (await _lock.EnterAsync())
{
await toComplete;
Console.WriteLine(i);
}
}
public static void Main(string[] args)
{
var tcs1 = new TaskCompletionSource<object>();
var tcs2 = new TaskCompletionSource<object>();
Task.Run(async () =>
{
var t1 = Test(1, tcs1.Task); // start first task
var t2 = Test(2, tcs2.Task); // start second task
tcs2.SetResult(null); // finish second first
tcs1.SetResult(null); // fiish last task
await Task.WhenAll(t1, t2); // will print: 1 and then 2
}).Wait();
}
}
The Test method takes will first enter the Async lock, then await the task toComplete and then write to the console.
We start two Test tasks ("1", and "2") and complete the second toComplete first. Without the AsyncLock the previous example prints: "2", "1". With the AsyncLock however the tasks are processed in the sequence they were started.
REMARKS: One last remark. This will achieve your processing order, but can be tricky sometimes. Using locks like this can easily lead to deadlocks which are hard to solve and harder to find in the first place. Use Locks very carefully.
EDIT: Here a usage example your your problem:
private readonly AsyncLock _lock = new AsyncLock();
public Textbox_TextChangedEvent()
{
GetStocks(texboxText); // every call is now "queued" after the previous one
}
public async Task GetStocks(string texboxText)
{
using(await _lock.EnterAsync())
{
IsBusy = true;
await Task.Run(() => { CreateCollection(texboxText); });
IsBusy = false;
}
}
An easy option, depending on the situation could be:
public async Task GetStocks(string texboxText)
{
Task.Run(() => {
IsBusy = true;
CreateCollection(texboxText);
IsBusy = false;
});
}
Related
If I have a task running on a worker thread and when it finds something wrong, is it possible to pause and wait for the user to intervene before continuing?
For example, suppose I have something like this:
async void btnStartTask_Click(object sender, EventArgs e)
{
await Task.Run(() => LongRunningTask());
}
// CPU-bound
bool LongRunningTask()
{
// Establish some connection here.
// Do some work here.
List<Foo> incorrectValues = GetIncorrectValuesFromAbove();
if (incorrectValues.Count > 0)
{
// Here, I want to present the "incorrect values" to the user (on the UI thread)
// and let them select whether to modify a value, ignore it, or abort.
var confirmedValues = WaitForUserInput(incorrectValues);
}
// Continue processing.
}
Is it possible to substitute WaitForUserInput() with something that runs on the UI thread, waits for the user's intervention, and then acts accordingly? If so, how? I'm not looking for complete code or anything; if someone could point me in the right direction, I would be grateful.
What you're looking for is almost exactly Progress<T>, except you want to have the thing that reports progress get a task back with some information that they can await and inspect the results of. Creating Progress<T> yourself isn't terribly hard., and you can reasonably easily adapt it so that it computes a result.
public interface IPrompt<TResult, TInput>
{
Task<TResult> Prompt(TInput input);
}
public class Prompt<TResult, TInput> : IPrompt<TResult, TInput>
{
private SynchronizationContext context;
private Func<TInput, Task<TResult>> prompt;
public Prompt(Func<TInput, Task<TResult>> prompt)
{
context = SynchronizationContext.Current ?? new SynchronizationContext();
this.prompt += prompt;
}
Task<TResult> IPrompt<TResult, TInput>.Prompt(TInput input)
{
var tcs = new TaskCompletionSource<TResult>();
context.Post(data => prompt((TInput)data)
.ContinueWith(task =>
{
if (task.IsCanceled)
tcs.TrySetCanceled();
if (task.IsFaulted)
tcs.TrySetException(task.Exception.InnerExceptions);
else
tcs.TrySetResult(task.Result);
}), input);
return tcs.Task;
}
}
Now you simply need to have an asynchronous method that accepts the data from the long running process and returns a task with whatever the user interface's response is.
You can use TaskCompletionSource to generate a task that can be awaited within the LongRunningTask.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class Program
{
private static event Action<string> Input;
public static async Task Main(string[] args)
{
var inputTask = InputTask();
var longRunningTask = Task.Run(() => LongRunningTask());
await Task.WhenAll(inputTask, longRunningTask);
}
private static async Task InputTask()
{
await Task.Yield();
while(true)
{
var input = await Console.In.ReadLineAsync();
Input?.Invoke(input);
}
}
static async Task<bool> LongRunningTask()
{
SomeExpensiveCall();
var incorrectValues = GetIncorrectValuesFromAbove();
if (incorrectValues.Count > 0)
{
var confirmedValues = await WaitForUserInput(incorrectValues).ConfigureAwait(false);
}
// Continue processing.
return true;
}
private static void SomeExpensiveCall()
{
}
private static Task<string> WaitForUserInput(IList<string> incorrectValues)
{
var taskCompletionSource = new TaskCompletionSource<string>();
Console.Write("Input Data: ");
try
{
void EventHandler(string input)
{
Input -= EventHandler;
taskCompletionSource.TrySetResult(input);
}
Input += EventHandler;
}
catch(Exception e)
{
taskCompletionSource.TrySetException(e);
}
return taskCompletionSource.Task;
}
private static IList<string> GetIncorrectValuesFromAbove()
{
return new List<string> { "Test" };
}
}
}
Of course in this example you could have just called await Console.In.ReadLineAsync() directly, but this code is to simulate an environment where you only have an event based API.
There are several ways to solve this problem, with the Control.Invoke being probably the most familiar. Here is a more TPL-ish approach. You start by declaring a UI related scheduler as a class field:
private TaskScheduler _uiScheduler;
Then initialize it:
public MyForm()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
Then you convert your synchronous LongRunning method to an asynchronous method. This means that it must return Task<bool> instead of bool. It must also have the async modifier, and by convention be named with the Async suffix:
async Task<bool> LongRunningAsync()
Finally you use the await operator in order to wait for the user's input, which will be a Task configured to run on the captured UI scheduler:
async Task<bool> LongRunningAsync()
{
// Establish some connection here.
// Do some work here.
List<Foo> incorrectValues = GetIncorrectValuesFromAbove();
if (incorrectValues.Count > 0)
{
// Here, I want to present the "incorrect values" to the user (on the UI thread)
// and let them select whether to modify a value, ignore it, or abort.
var confirmedValues = await Task.Factory.StartNew(() =>
{
return WaitForUserInput(incorrectValues);
}, default, TaskCreationOptions.None, _uiScheduler);
}
// Continue processing.
}
Starting the long running task is the same as before. The Task.Run understands async delegates, so you don't have to do something special after making the method async.
var longRunningTask = Task.Run(() => LongRunningAsync());
This should be enough, provided that you just intend to show a dialog box to the user. The Form.ShowDialog is a blocking method, so the WaitForUserInput method needs not to be asynchronous. If you had to allow the user to interact freely with the main form, the problem would be much more difficult to solve.
Another example using Invoke() and a ManualResetEvent. Let me know if you need help with the form code; setting up a constructor, using DialogResult, or creating a property to hold the "confirmedValues":
bool LongRunningTask()
{
// Establish some connection here.
// Do some work here.
List<Foo> incorrectValues = GetIncorrectValuesFromAbove();
var confirmedValues;
if (incorrectValues.Count > 0)
{
DialogResult result;
ManualResetEvent mre = new ManualResetEvent(false);
this.Invoke((MethodInvoker)delegate
{
// pass in incorrectValues to the form
// you'll have to build a constructor in it to accept them
frmSomeForm frm = new frmSomeForm(incorrectValues);
result = frm.ShowDialog();
if (result == DialogResult.OK)
{
confirmedValues = frm.confirmedValues; // get the confirmed values somehow
}
mre.Set(); // release the block below
});
mre.WaitOne(); // blocks until "mre" is set
}
// Continue processing.
}
Let's say I have two methods in my class MethodA and MethodB. Those methods are run in async, and I don't want that they run at the same time.
In the app, MethodA is called by the user, but MethodB is run by a Timer in a background thread.
How I see the implementation in C#ish pseudocode:
class MyClass
{
private static object _lock = new object();
public async Task MethodA()
{
lock(_lock)
{
await DoSomeStuff();
}
}
public async Task MethodB()
{
if(Monitor.IsEntered(_lock)
return;
lock(_lock)
{
await DoSomeStuff();
}
}
}
So first question is - is above approach correct? I guess that's more a question for https://codereview.stackexchange.com/.
So the second questions is - assuming that the approach is correct, how I can unit test, that MethodA waits for MethodB, and that MethodB doesn't run when MethodA is running? Or how can I refactor it so that's testable?
EDIT: accordingly to comments, changed from using flags to a lock.
Boolean flags are the obsolete way to synchronize two threads. It causes race conditions when one thread can read a value of false while other thread is updating the value to true;
Since your case it not straightforward (B shouldn't way for B to end, while A should wait), then I would change the class use a Semaphore like this:
public class MyClass
{
private SemaphoreSlim semaphore = new SemaphoreSlim(1);
public async Task MethodA()
{
await semaphore.WaitAsync();
await DoSomeStuff();
semaphore.Release();
}
public async Task MethodB()
{
bool success = await semaphore.WaitAsync(1);
if (!success)
return;
await DoSomeStuff();
await Task.Delay(TimeSpan.FromSeconds(5));
semaphore.Release();
}
}
I would consider putting all that in try..catch..finally block and release the semaphore in the finally block, but i'm trying to keep it simple while you can add that yourself.
Unit testing:
This is not straight forward to test. Taking threads into account, you might need to repeat the test multiple times to reach a case of failure. You might need to introduce an overload for method A that waits for some times, which might prove that method B is waiting for it. Here is the test. To test the case of failure, change new SemaphoreSlim(1); to new SemaphoreSlim(2); and the test would fail because MethodB would start before MethodA ends.
[TestMethod]
public async Task TestingMyClassThreadSync()
{
int repeatTimes = 100;
int counter = 0;
while (counter++ <= repeatTimes)
{
MyClass myClass = new MyClass();
Task tA = myClass.MethodA();
Task tB = myClass.MethodB();
Task finishedTask = await Task.WhenAny(tA, tB);
bool bFinishedBeforeA = finishedTask == tA;
if (bFinishedBeforeA)
Assert.Fail();
}
}
I would introduce an overload:
public async Task MethodA(long waitMilliseconds = 0)
{
await semaphore.WaitAsync();
await DoSomeStuff();
await Task.Delay(waitMilliseconds);
semaphore.Release();
}
Then Call it from unit testing as MethodA(5000);
I am trying to execute parallel methods, but Form gets stuck whenever
I call them.
Please correct what I am doing wrong. Here is the Code:
public partial class Form1 : Form
{
private async void button1_Click(object sender, EventArgs e)
{
var itemList = new List<string>() { "Field1", "Field2", "Field3" };
await Task.WhenAll(itemList.Select(item =>
new WorkToDo(item).StartWork()
));
}
}
public class WorkToDo
{
private string id;
public WorkToDo(string id)
{
this.id = id;
}
public async Task<bool> StartWork()
{
Calculate();
Analyze();
SomeToDo();
var result = Save();
await Task.Delay(100);
return result;
}
private bool Calculate()
{
//Some complex and time taking calculation will be here
return true;
}
private bool Analyze()
{
//Some complex and time taking calculation will be here
return true;
}
private bool SomeToDo()
{
//Some complex and time taking calculation will be here
return true;
}
private bool Save()
{
//Some complex and time taking calculation will be here
return true;
}
}
You need to remember that normal async / await will still be performed on the UI thread.
So to be sure that a real long action is pushed to a background thread, you need to wrap it in Task.Run... like Task.Run(() => Task.WhenAll(tasks));
To complete the question a bit more ( seeing the other answer available ), Task.Run usage is not something to take lightly. It all depends on what sort of code needs to be wrapped.
There is a good write up series on this up on the blog of Stephen Cleary here http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-even-in.html so take some time to go through this and see what fits your project.
Or look here for some other details from Stephen https://stackoverflow.com/a/18015586
The problem that you have is that StartWork claims to be asynchronous, and yet it isn't. It does all of it's work synchronously.
Marking a method as async doesn't make it asynchronous. it just allows you to use the await keyword from within that method. If you perform long running synchronous operations from an async method then that method is still going to be doing that work synchronously.
There are really two approaches to take here. If there are some number of the things done in StartWork that really are inherently asynchronous, then you need to wrap whatever synchronous CPU bound work you have in calls to Task.Run so that the synchronous work that you have can be done asynchronously in a thread pool thread.
If there is no inherently asynchronous operations you have to do in StartWork then make the method clearly synchronous. Have it return a boolean, not a Task, and adjust the name to reflect the fact that it is synchronous. Then have the caller that calls it use Task.Run to do that whole operation on a thread pool thread asynchronously.
Having StartWork falsely claim to be asynchronous, and then still using Task.Run to do the purportedly asynchronous work in another thread is going to be extremely confusing to other readers of your code, as there should be no reason to offload an asynchronous method to a non-UI thread.
IMHO, If you are using Async operation you don't need Task.Run() if you have Sync Task and do it asynchronously you need Task.Run()
If you are using normal synchronous process just return Task<T> and use this Task.Run(()) to use background thread to process. See this answer
private async void button1_Click(object sender, EventArgs e)
{
var itemList = new List<string>() { "Field1", "Field2", "Field3" }; // more than 50 items
Task.Run(() => Task.WhenAll(tasks));
}
public class WorkToDo
{
private string id;
public WorkToDo(string id)
{
this.id = id;
}
public async Task<bool> StartWork()
{
var t1 = Calculate();
var t2 = Analyze();
var t3 = SomeToDo();
//Assuming you need to do all this before you save
// so wait for the all.
await Task.WhenAll(t1,t2,t3);
var result = await Save();
return result;
}
private async Task<bool> Calculate()
{
//Some complex and time taking calculation will be here
//Assuming here you have some DoAsync() method
return true;
}
private async Task<bool> Analyze()
{
//Some complex and time taking calculation will be here
return true;
}
private async Task<bool> SomeToDo()
{
//Some complex and time taking calculation will be here
return true;
}
private async Task<bool> Save()
{
//Some complex and time taking calculation will be here
return true;
}
Using WhenAll() has some advantage like propagating all error at once, see this
I'm wondering if there exists an implementation/wrapper for ConcurrentQueue, similar to BlockingCollection where taking from the collection does not block, but is instead asynchronous and will cause an async await until an item is placed in the queue.
I've come up with my own implementation, but it does not seem to be performing as expected. I'm wondering if I'm reinventing something that already exists.
Here's my implementation:
public class MessageQueue<T>
{
ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
ConcurrentQueue<TaskCompletionSource<T>> waitingQueue =
new ConcurrentQueue<TaskCompletionSource<T>>();
object queueSyncLock = new object();
public void Enqueue(T item)
{
queue.Enqueue(item);
ProcessQueues();
}
public async Task<T> Dequeue()
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
waitingQueue.Enqueue(tcs);
ProcessQueues();
return tcs.Task.IsCompleted ? tcs.Task.Result : await tcs.Task;
}
private void ProcessQueues()
{
TaskCompletionSource<T> tcs=null;
T firstItem=default(T);
while (true)
{
bool ok;
lock (queueSyncLock)
{
ok = waitingQueue.TryPeek(out tcs) && queue.TryPeek(out firstItem);
if (ok)
{
waitingQueue.TryDequeue(out tcs);
queue.TryDequeue(out firstItem);
}
}
if (!ok) break;
tcs.SetResult(firstItem);
}
}
}
I don't know of a lock-free solution, but you can take a look at the new Dataflow library, part of the Async CTP. A simple BufferBlock<T> should suffice, e.g.:
BufferBlock<int> buffer = new BufferBlock<int>();
Production and consumption are most easily done via extension methods on the dataflow block types.
Production is as simple as:
buffer.Post(13);
and consumption is async-ready:
int item = await buffer.ReceiveAsync();
I do recommend you use Dataflow if possible; making such a buffer both efficient and correct is more difficult than it first appears.
Simple approach with C# 8.0 IAsyncEnumerable and Dataflow library
// Instatiate an async queue
var queue = new AsyncQueue<int>();
// Then, loop through the elements of queue.
// This loop won't stop until it is canceled or broken out of
// (for that, use queue.WithCancellation(..) or break;)
await foreach(int i in queue) {
// Writes a line as soon as some other Task calls queue.Enqueue(..)
Console.WriteLine(i);
}
With an implementation of AsyncQueue as follows:
public class AsyncQueue<T> : IAsyncEnumerable<T>
{
private readonly SemaphoreSlim _enumerationSemaphore = new SemaphoreSlim(1);
private readonly BufferBlock<T> _bufferBlock = new BufferBlock<T>();
public void Enqueue(T item) =>
_bufferBlock.Post(item);
public async IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token = default)
{
// We lock this so we only ever enumerate once at a time.
// That way we ensure all items are returned in a continuous
// fashion with no 'holes' in the data when two foreach compete.
await _enumerationSemaphore.WaitAsync();
try {
// Return new elements until cancellationToken is triggered.
while (true) {
// Make sure to throw on cancellation so the Task will transfer into a canceled state
token.ThrowIfCancellationRequested();
yield return await _bufferBlock.ReceiveAsync(token);
}
} finally {
_enumerationSemaphore.Release();
}
}
}
There is an official way to do this now: System.Threading.Channels. It's built into the core runtime on .NET Core 3.0 and higher (including .NET 5.0 and 6.0), but it's also available as a NuGet package on .NET Standard 2.0 and 2.1. You can read through the docs here.
var channel = System.Threading.Channels.Channel.CreateUnbounded<int>();
To enqueue work:
// This will succeed and finish synchronously if the channel is unbounded.
channel.Writer.TryWrite(42);
To complete the channel:
channel.Writer.TryComplete();
To read from the channel:
var i = await channel.Reader.ReadAsync();
Or, if you have .NET Core 3.0 or higher:
await foreach (int i in channel.Reader.ReadAllAsync())
{
// whatever processing on i...
}
One simple and easy way to implement this is with a SemaphoreSlim:
public class AwaitableQueue<T>
{
private SemaphoreSlim semaphore = new SemaphoreSlim(0);
private readonly object queueLock = new object();
private Queue<T> queue = new Queue<T>();
public void Enqueue(T item)
{
lock (queueLock)
{
queue.Enqueue(item);
semaphore.Release();
}
}
public T WaitAndDequeue(TimeSpan timeSpan, CancellationToken cancellationToken)
{
semaphore.Wait(timeSpan, cancellationToken);
lock (queueLock)
{
return queue.Dequeue();
}
}
public async Task<T> WhenDequeue(TimeSpan timeSpan, CancellationToken cancellationToken)
{
await semaphore.WaitAsync(timeSpan, cancellationToken);
lock (queueLock)
{
return queue.Dequeue();
}
}
}
The beauty of this is that the SemaphoreSlim handles all of the complexity of implementing the Wait() and WaitAsync() functionality. The downside is that queue length is tracked by both the semaphore and the queue itself, and they both magically stay in sync.
My atempt (it have an event raised when a "promise" is created, and it can be used by an external producer to know when to produce more items):
public class AsyncQueue<T>
{
private ConcurrentQueue<T> _bufferQueue;
private ConcurrentQueue<TaskCompletionSource<T>> _promisesQueue;
private object _syncRoot = new object();
public AsyncQueue()
{
_bufferQueue = new ConcurrentQueue<T>();
_promisesQueue = new ConcurrentQueue<TaskCompletionSource<T>>();
}
/// <summary>
/// Enqueues the specified item.
/// </summary>
/// <param name="item">The item.</param>
public void Enqueue(T item)
{
TaskCompletionSource<T> promise;
do
{
if (_promisesQueue.TryDequeue(out promise) &&
!promise.Task.IsCanceled &&
promise.TrySetResult(item))
{
return;
}
}
while (promise != null);
lock (_syncRoot)
{
if (_promisesQueue.TryDequeue(out promise) &&
!promise.Task.IsCanceled &&
promise.TrySetResult(item))
{
return;
}
_bufferQueue.Enqueue(item);
}
}
/// <summary>
/// Dequeues the asynchronous.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns></returns>
public Task<T> DequeueAsync(CancellationToken cancellationToken)
{
T item;
if (!_bufferQueue.TryDequeue(out item))
{
lock (_syncRoot)
{
if (!_bufferQueue.TryDequeue(out item))
{
var promise = new TaskCompletionSource<T>();
cancellationToken.Register(() => promise.TrySetCanceled());
_promisesQueue.Enqueue(promise);
this.PromiseAdded.RaiseEvent(this, EventArgs.Empty);
return promise.Task;
}
}
}
return Task.FromResult(item);
}
/// <summary>
/// Gets a value indicating whether this instance has promises.
/// </summary>
/// <value>
/// <c>true</c> if this instance has promises; otherwise, <c>false</c>.
/// </value>
public bool HasPromises
{
get { return _promisesQueue.Where(p => !p.Task.IsCanceled).Count() > 0; }
}
/// <summary>
/// Occurs when a new promise
/// is generated by the queue
/// </summary>
public event EventHandler PromiseAdded;
}
It may be overkill for your use case (given the learning curve), but Reactive Extentions provides all the glue you could ever want for asynchronous composition.
You essentially subscribe to changes and they are pushed to you as they become available, and you can have the system push the changes on a separate thread.
Check out https://github.com/somdoron/AsyncCollection, you can both dequeue asynchronously and use C# 8.0 IAsyncEnumerable.
The API is very similar to BlockingCollection.
AsyncCollection<int> collection = new AsyncCollection<int>();
var t = Task.Run(async () =>
{
while (!collection.IsCompleted)
{
var item = await collection.TakeAsync();
// process
}
});
for (int i = 0; i < 1000; i++)
{
collection.Add(i);
}
collection.CompleteAdding();
t.Wait();
With IAsyncEnumeable:
AsyncCollection<int> collection = new AsyncCollection<int>();
var t = Task.Run(async () =>
{
await foreach (var item in collection)
{
// process
}
});
for (int i = 0; i < 1000; i++)
{
collection.Add(i);
}
collection.CompleteAdding();
t.Wait();
Here's the implementation I'm currently using.
public class MessageQueue<T>
{
ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
ConcurrentQueue<TaskCompletionSource<T>> waitingQueue =
new ConcurrentQueue<TaskCompletionSource<T>>();
object queueSyncLock = new object();
public void Enqueue(T item)
{
queue.Enqueue(item);
ProcessQueues();
}
public async Task<T> DequeueAsync(CancellationToken ct)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
ct.Register(() =>
{
lock (queueSyncLock)
{
tcs.TrySetCanceled();
}
});
waitingQueue.Enqueue(tcs);
ProcessQueues();
return tcs.Task.IsCompleted ? tcs.Task.Result : await tcs.Task;
}
private void ProcessQueues()
{
TaskCompletionSource<T> tcs = null;
T firstItem = default(T);
lock (queueSyncLock)
{
while (true)
{
if (waitingQueue.TryPeek(out tcs) && queue.TryPeek(out firstItem))
{
waitingQueue.TryDequeue(out tcs);
if (tcs.Task.IsCanceled)
{
continue;
}
queue.TryDequeue(out firstItem);
}
else
{
break;
}
tcs.SetResult(firstItem);
}
}
}
}
It works good enough, but there's quite a lot of contention on queueSyncLock, as I am making quite a lot of use of the CancellationToken to cancel some of the waiting tasks. Of course, this leads to considerably less blocking I would see with a BlockingCollection but...
I'm wondering if there is a smoother, lock free means of achieving the same end
Well 8 years later I hit this very question and was about to implement the MS AsyncQueue<T> class found in nuget package/namespace: Microsoft.VisualStudio.Threading
Thanks to #Theodor Zoulias for mentioning this api may be outdated and the DataFlow lib would be a good alternative.
So I edited my AsyncQueue<> implementation to use BufferBlock<>. Almost the same but works better.
I use this in an AspNet Core background thread and it runs fully async.
protected async Task MyRun()
{
BufferBlock<MyObj> queue = new BufferBlock<MyObj>();
Task enqueueTask = StartDataIteration(queue);
while (await queue.OutputAvailableAsync())
{
var myObj = queue.Receive();
// do something with myObj
}
}
public async Task StartDataIteration(BufferBlock<MyObj> queue)
{
var cursor = await RunQuery();
while(await cursor.Next()) {
queue.Post(cursor.Current);
}
queue.Complete(); // <<< signals the consumer when queue.Count reaches 0
}
I found that using the queue.OutputAvailableAsync() fixed the issue that I had with AsyncQueue<> -- trying to determine when the queue was complete and not having to inspect the dequeue task.
You could just use a BlockingCollection ( using the default ConcurrentQueue ) and wrap the call to Take in a Task so you can await it:
var bc = new BlockingCollection<T>();
T element = await Task.Run( () => bc.Take() );
I am looking for the correct way/structure to create a loop in a Thread/Task...
The reason for this is, i need to check the DB every 15sec for report requests.
This is what i tried so far, but i get OutOfMemoryException:
private void ViewBase_Loaded(object sender, RoutedEventArgs e)
{
//On my main view loaded start thread to check report requests.
Task.Factory.StartNew(() => CreateAndStartReportRequestTask());
}
private void CreateAndStartReportRequestTask()
{
bool noRequest = false;
do
{
//Starting thread to Check Report Requests And Generate Reports
//Also need the ability to Wait/Sleep when there are noRequest.
reportRequestTask = Task.Factory.StartNew(() => noRequest = CheckReportRequestsAndGenerateReports());
if (noRequest)
{
//Sleep 15sec
reportRequestTask.Wait(15000);
reportRequestTask = null;
}
else
{
if (reportRequestTask.IsCompleted)
{
reportRequestTask = null;
}
else
{
//Don't want the loop to continue until the first request is done
//Reason for this is, losts of new threads being create in CheckReportRequestsAndGenerateReports()
//Looping until first request is done.
do
{
} while (!reportRequestTask.IsCompleted);
reportRequestTask = null;
}
}
} while (true);
}
private bool CheckReportRequestsAndGenerateReports()
{
var possibleReportRequest = //Some linq query to check for new requests
if (possibleReportRequest != null)
{
//Processing report here - lots of new threads/task in here as well
return false;
}
else
{
return true;
}
}
What am i doing wrong?
Is this correct way or am i total off?
EDIT:
Most important, my UI must still be responsive!
Something like this would work:
var cancellationTokenSource = new CancellationTokenSource();
var task = Repeat.Interval(
TimeSpan.FromSeconds(15),
() => CheckDatabaseForNewReports(), cancellationTokenSource.Token);
The Repeat class looks like this:
internal static class Repeat
{
public static Task Interval(
TimeSpan pollInterval,
Action action,
CancellationToken token)
{
// We don't use Observable.Interval:
// If we block, the values start bunching up behind each other.
return Task.Factory.StartNew(
() =>
{
for (;;)
{
if (token.WaitCancellationRequested(pollInterval))
break;
action();
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}
static class CancellationTokenExtensions
{
public static bool WaitCancellationRequested(
this CancellationToken token,
TimeSpan timeout)
{
return token.WaitHandle.WaitOne(timeout);
}
}
Sounds like you want something like this. Please correct me if I am misinterpretting your intentions...
First, in your kick-off, set as a long running task so it doesn't consume a thread from the thread pool but creates a new one...
private void ViewBase_Loaded(object sender, RoutedEventArgs e)
{
// store this references as a private member, call Cancel() on it if UI wants to stop
_cancelationTokenSource = new CancellationTokenSource();
new Task(() => CreateAndStartReportRequestTask(), _cancelationTokenSource.Token, TaskCreationOptions.LongRunning).Start();
}
Then, in your report watching thread, loop until IsCancelRequested has been set. If there is no work, just wait on the cancel token for 15 seconds (this way if cancelled will wake sooner).
private bool CheckReportRequestsAndGenerateReports()
{
while (!_cancellationTokenSource.Token.IsCancelRequested)
{
var possibleReportRequest = //Some linq query
var reportRequestTask = Task.Factory.StartNew(() => noRequest = CheckReportRequestsAndGenerateReports(), _cancellationTokenSource.Token);
if (noRequest)
{
// it looks like if no request, you want to sleep 15 seconds, right?
// so we'll wait to see if cancelled in next 15 seconds.
_cancellationTokenSource.Token.WaitHandle.WaitOne(15000);
}
else
{
// otherwise, you just want to wait till the task is completed, right?
reportRequestTask.Wait(_cancellationTokenSource.Token);
}
}
}
I'd also be wary of having your task kick off more tasks. I have a feeling you are spinning up so many you're consuming too many resources. I think the main reason your program was failing was that you had:
if (noRequest)
{
reportRequestTask.Wait(15000);
reportRequestTask = null;
}
This will return immediately and not wait 15s, because the thread is already complete at this point. Switching it to the cancel token (or a Thread.Sleep(), but then you can't abort it as easily) will give you the processing wait you need.
Hope this helps, let me know if i'm off on my assumptions.
I've made a work-around starting from #Roger's answer. (A friend of mine has given good advices regarding this too)... I copy it here I guess it could be useful:
/// <summary>
/// Recurrent Cancellable Task
/// </summary>
public static class RecurrentCancellableTask
{
/// <summary>
/// Starts a new task in a recurrent manner repeating it according to the polling interval.
/// Whoever use this method should protect himself by surrounding critical code in the task
/// in a Try-Catch block.
/// </summary>
/// <param name="action">The action.</param>
/// <param name="pollInterval">The poll interval.</param>
/// <param name="token">The token.</param>
/// <param name="taskCreationOptions">The task creation options</param>
public static void StartNew(Action action,
TimeSpan pollInterval,
CancellationToken token,
TaskCreationOptions taskCreationOptions = TaskCreationOptions.None)
{
Task.Factory.StartNew(
() =>
{
do
{
try
{
action();
if (token.WaitHandle.WaitOne(pollInterval)) break;
}
catch
{
return;
}
}
while (true);
},
token,
taskCreationOptions,
TaskScheduler.Default);
}
}
feeling adventurous?
internal class Program
{
private static void Main(string[] args)
{
var ct = new CancellationTokenSource();
new Task(() => Console.WriteLine("Running...")).Repeat(ct.Token, TimeSpan.FromSeconds(1));
Console.WriteLine("Starting. Hit Enter to Stop.. ");
Console.ReadLine();
ct.Cancel();
Console.WriteLine("Stopped. Hit Enter to exit.. ");
Console.ReadLine();
}
}
public static class TaskExtensions
{
public static void Repeat(this Task taskToRepeat, CancellationToken cancellationToken, TimeSpan intervalTimeSpan)
{
var action = taskToRepeat
.GetType()
.GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(taskToRepeat) as Action;
Task.Factory.StartNew(() =>
{
while (true)
{
if (cancellationToken.WaitHandle.WaitOne(intervalTimeSpan))
break;
if (cancellationToken.IsCancellationRequested)
break;
Task.Factory.StartNew(action, cancellationToken);
}
}, cancellationToken);
}
}