I have an application running an observable interval with multiple observers. Every 0.5s the interval loads some XML data from a web server, then the observers do some application specific processing on a background thread. Once the data isn't needed anymore, the subscriptions and the interval observable get disposed, so observer's OnNext/OnCompleted/OnError won't be called anymore. So far so good.
My problem: In some rare cases it is possible, that after calling Dispose my observer's OnNext method is still running! Before proceeding to further operations after disposing, I would like to ensure that OnNext has been completed.
My current solution: I've introduced a locker field in my observer class (see code). After disposing I try to acquire a lock and continue only after the lock has been acquired. While this solution works (?), it somehow just feels wrong to me.
Question: Is there a more elegant, more "Rx Way" to solve this problem?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace RxExperimental
{
internal sealed class MyXmlDataFromWeb
{
public string SomeXmlDataFromWeb { get; set; }
}
internal sealed class MyObserver : IObserver<MyXmlDataFromWeb>
{
private readonly object _locker = new object();
private readonly string _observerName;
public MyObserver(string observerName) {
this._observerName = observerName;
}
public object Locker {
get { return this._locker; }
}
public void OnCompleted() {
lock (this._locker) {
Console.WriteLine("{0}: Completed.", this._observerName);
}
}
public void OnError(Exception error) {
lock (this._locker) {
Console.WriteLine("{0}: An error occured: {1}", this._observerName, error.Message);
}
}
public void OnNext(MyXmlDataFromWeb value) {
lock (this._locker) {
Console.WriteLine(" {0}: OnNext running on thread {1}... ", this._observerName, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(" {0}: XML received: {1}", this._observerName, value.SomeXmlDataFromWeb);
Thread.Sleep(5000); // simulate some long running operation
Console.WriteLine(" {0}: OnNext running on thread {1}... Done.", this._observerName, Thread.CurrentThread.ManagedThreadId);
}
}
}
internal sealed class Program
{
private static void Main() {
const int interval = 500;
//
var dataSource = Observable.Interval(TimeSpan.FromMilliseconds(interval), NewThreadScheduler.Default).Select(_ => {
var data = new MyXmlDataFromWeb {
SomeXmlDataFromWeb = String.Format("<timestamp>{0:yyyy.MM.dd HH:mm:ss:fff}</timestamp>", DateTime.Now)
};
return data;
}).Publish();
//
var observer1 = new MyObserver("Observer 1");
var observer2 = new MyObserver("Observer 2");
//
var subscription1 = dataSource.ObserveOn(NewThreadScheduler.Default).Subscribe(observer1);
var subscription2 = dataSource.ObserveOn(NewThreadScheduler.Default).Subscribe(observer2);
//
var connection = dataSource.Connect();
//
Console.WriteLine("Press any key to cancel ...");
Console.ReadLine();
//
subscription1.Dispose();
subscription2.Dispose();
connection.Dispose();
//
lock (observer1.Locker) {
Console.WriteLine("Observer 1 completed.");
}
lock (observer2.Locker) {
Console.WriteLine("Observer 2 completed.");
}
//
Console.WriteLine("Can only be executed, after all observers completed.");
}
}
}
Yes, there's a more Rx-way of doing this.
The first observation is that unsubscribing from an observable stream is essentially independent from what is currently happening within the observer. There's not really any feedback. Since you have the requirement that you know definitively when the observations have ended, you need to model this into your observable stream. In other words, instead of unsubscribing from the stream, you should complete the stream so you will be able to observe the OnComplete event. In your case, you can use TakeUntil to end the observable instead of unsubscribing from it.
The second observation is that your main program needs to observe when your "observer" finishes its work. But since you made your "observer" an actual IObservable, you don't really have a way to do this. This is a common source of confusion I see when people first start using Rx. If you model your "observer" as just another link in the observable chain, then your main program can observer. Specifically, your "observer" is nothing more than a mapping operation (with side effects) that maps incoming Xml data into "done" messages.
So if you refactor your code, you can get what you want...
public class MyObserver
{
private readonly string _name;
public MyObserver(string name) { _name = name; }
public IObservable<Unit> Handle(IObservable<MyXmlDataFromWeb source)
{
return source.Select(value =>
{
Thread.Sleep(5000); // simulate work
return Unit.Default;
});
}
}
// main
var endSignal = new Subject<Unit>();
var dataSource = Observable
.Interval(...)
.Select(...)
.TakeUntil(endSignal)
.Publish();
var observer1 = new MyObserver("Observer 1");
var observer2 = new MyObserver("Observer 2");
var results1 = observer1.Handle(dataSource.ObserveOn(...));
var results2 = observer2.Handle(dataSource.ObserveOn(...));
// since you just want to know when they are all done
// just merge them.
// use ToTask() to subscribe them and collect the results
// as a Task
var processingDone = results1.Merge(results2).Count().ToTask();
dataSource.Connect();
Console.WriteLine("Press any key to cancel ...");
Console.ReadLine();
// end the stream
endSignal.OnNext(Unit.Default);
// wait for the processing to complete.
// use await, or Task.Result
var numProcessed = await processingDone;
Related
Subject.HasObservers is not immediately true in the sample code attached for an undetermined number of ticks. If I take out the SubscribeOn(), HasObservers is always true, so I know it's to do with IScheduler initialization.
This was causing a problem in our production software where the first few calls to OnNext() were going nowhere despite a guarantee that the IDisposable subscription variable had been initialized before the thread that called OnNext() was allowed to proceed. Is this a bug in RX?
What are other ways to use System.Reactive classes to guarantee the subscription is setup with a scheduler without polling?
I have tried Subject.Synchronize(), but that made no difference.
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
var source = new Subject<long>();
IDisposable subscription = source
.SubscribeOn(ThreadPoolScheduler.Instance)
.Subscribe(Console.WriteLine);
// 0 and 668,000 ticks for subscription setup, but rarely 0.
int iterations = 0;
while (!source.HasObservers)
{
iterations++;
Thread.SpinWait(1);
}
// Next line would rarely output to Console without while loop
source.OnNext(iterations);
subscription.Dispose();
source.Dispose();
}
}
I expected Subject.HasObservers to be true without polling.
As I understand, the problem is that your subscription is done asynchronously: the call is not blocked, so the real subscription will be done later on other thread.
I didn't find the exact way of knowing if the subscription has really landed (it might be even not possible at all). If your problem is the race between the first OnNext and subscription, than maybe you need to convert your Observable into a Connectable Observable using Replay() + Connect(). This way you'll ensure that every subscriber gets exactly the same sequence.
using (var source = new Subject<long>())
{
var connectableSource = source.Replay();
connectableSource.Connect();
using (var subscription = connectableSource
.SubscribeOn(ThreadPoolScheduler.Instance)
.Subscribe(Console.WriteLine))
{
source.OnNext(42); // outputs 42 always
Console.ReadKey(false);
}
}
In my code I still need Console.ReadKey because of the race between subscription done on the other thread and unsubscription.
The solution I came up with for now that I'm hoping someone can improve upon:
public class SubscribedSubject<T> : ISubject<T>, IDisposable
{
private readonly Subject<T> _subject = new Subject<T>();
private readonly ManualResetEventSlim _subscribed = new ManualResetEventSlim();
public bool HasObservers => _subject.HasObservers;
public void Dispose() => _subject.Dispose();
public void OnCompleted() => Wait().OnCompleted();
public void OnError(Exception error) => Wait().OnError(error);
public void OnNext(T value) => Wait().OnNext(value);
public IDisposable Subscribe(IObserver<T> observer)
{
IDisposable disposable = _subject.Subscribe(observer);
_subscribed.Set();
return disposable;
}
private Subject<T> Wait()
{
_subscribed.Wait();
return _subject;
}
}
Example use:
using (var source = new SubscribedSubject<long>())
{
using (source
.SubscribeOn(ThreadPoolScheduler.Instance)
.Subscribe(Console.WriteLine))
{
source.OnNext(42);
Console.ReadKey();
}
}
What is the best approach to creating a simple multithread safe logging class? Is something like this sufficient? How would I purge the log when it's initially created?
public class Logging
{
public Logging()
{
}
public void WriteToLog(string message)
{
object locker = new object();
lock(locker)
{
StreamWriter SW;
SW=File.AppendText("Data\\Log.txt");
SW.WriteLine(message);
SW.Close();
}
}
}
public partial class MainWindow : Window
{
public static MainWindow Instance { get; private set; }
public Logging Log { get; set; }
public MainWindow()
{
Instance = this;
Log = new Logging();
}
}
Here is a sample for a Log implemented with the Producer/Consumer pattern (with .Net 4) using a BlockingCollection. The interface is :
namespace Log
{
public interface ILogger
{
void WriteLine(string msg);
void WriteError(string errorMsg);
void WriteError(string errorObject, string errorAction, string errorMsg);
void WriteWarning(string errorObject, string errorAction, string errorMsg);
}
}
and the full class code is here :
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Log
{
// Reentrant Logger written with Producer/Consumer pattern.
// It creates a thread that receives write commands through a Queue (a BlockingCollection).
// The user of this log has just to call Logger.WriteLine() and the log is transparently written asynchronously.
public class Logger : ILogger
{
BlockingCollection<Param> bc = new BlockingCollection<Param>();
// Constructor create the thread that wait for work on .GetConsumingEnumerable()
public Logger()
{
Task.Factory.StartNew(() =>
{
foreach (Param p in bc.GetConsumingEnumerable())
{
switch (p.Ltype)
{
case Log.Param.LogType.Info:
const string LINE_MSG = "[{0}] {1}";
Console.WriteLine(String.Format(LINE_MSG, LogTimeStamp(), p.Msg));
break;
case Log.Param.LogType.Warning:
const string WARNING_MSG = "[{3}] * Warning {0} (Action {1} on {2})";
Console.WriteLine(String.Format(WARNING_MSG, p.Msg, p.Action, p.Obj, LogTimeStamp()));
break;
case Log.Param.LogType.Error:
const string ERROR_MSG = "[{3}] *** Error {0} (Action {1} on {2})";
Console.WriteLine(String.Format(ERROR_MSG, p.Msg, p.Action, p.Obj, LogTimeStamp()));
break;
case Log.Param.LogType.SimpleError:
const string ERROR_MSG_SIMPLE = "[{0}] *** Error {1}";
Console.WriteLine(String.Format(ERROR_MSG_SIMPLE, LogTimeStamp(), p.Msg));
break;
default:
Console.WriteLine(String.Format(LINE_MSG, LogTimeStamp(), p.Msg));
break;
}
}
});
}
~Logger()
{
// Free the writing thread
bc.CompleteAdding();
}
// Just call this method to log something (it will return quickly because it just queue the work with bc.Add(p))
public void WriteLine(string msg)
{
Param p = new Param(Log.Param.LogType.Info, msg);
bc.Add(p);
}
public void WriteError(string errorMsg)
{
Param p = new Param(Log.Param.LogType.SimpleError, errorMsg);
bc.Add(p);
}
public void WriteError(string errorObject, string errorAction, string errorMsg)
{
Param p = new Param(Log.Param.LogType.Error, errorMsg, errorAction, errorObject);
bc.Add(p);
}
public void WriteWarning(string errorObject, string errorAction, string errorMsg)
{
Param p = new Param(Log.Param.LogType.Warning, errorMsg, errorAction, errorObject);
bc.Add(p);
}
string LogTimeStamp()
{
DateTime now = DateTime.Now;
return now.ToShortTimeString();
}
}
}
In this sample, the internal Param class used to pass information to the writing thread through the BlockingCollection is :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Log
{
internal class Param
{
internal enum LogType { Info, Warning, Error, SimpleError };
internal LogType Ltype { get; set; } // Type of log
internal string Msg { get; set; } // Message
internal string Action { get; set; } // Action when error or warning occurs (optional)
internal string Obj { get; set; } // Object that was processed whend error or warning occurs (optional)
internal Param()
{
Ltype = LogType.Info;
Msg = "";
}
internal Param(LogType logType, string logMsg)
{
Ltype = logType;
Msg = logMsg;
}
internal Param(LogType logType, string logMsg, string logAction, string logObj)
{
Ltype = logType;
Msg = logMsg;
Action = logAction;
Obj = logObj;
}
}
}
No, you're creating a new lock object every time the method is called. If you want to ensure that only one thread at a time can execute the code in that function, then move locker out of the function, either to an instance or a static member. If this class is instantiated every time an entry is to be written, then locker should probably be static.
public class Logging
{
public Logging()
{
}
private static readonly object locker = new object();
public void WriteToLog(string message)
{
lock(locker)
{
StreamWriter SW;
SW=File.AppendText("Data\\Log.txt");
SW.WriteLine(message);
SW.Close();
}
}
}
Creating a thread-safe logging implementation using a single monitor (lock) is unlikely to yield positive results. While you could do this correctly, and several answers have been posted showing how, it would have a dramatic negative effect on performance since each object doing logging would have to synchronize with every other object doing logging. Get more than one or two threads doing this at the same time and suddenly you may spend more time waiting than processing.
The other problem you run into with the single monitor approach is that you have no guarantee that threads will acquire the lock in the order they initially requested it. So, the log entries may essentially appear out of order. That can be frustrating if you're using this for trace logging.
Multi-threading is hard. Approaching it lightly will always lead to bugs.
One approach to this problem would be to implement the Producer/Consumer pattern, wherein callers to the logger only need to write to a memory buffer and return immediately rather than wait for the logger to write to disk, thus drastically reducing the performance penalty. The logging framework would, on a separate thread, consume the log data and persist it.
you need to declare the sync object at the class level:
public class Logging
{
private static readonly object locker = new object();
public Logging()
{
}
public void WriteToLog(string message)
{
lock(locker)
{
StreamWriter sw;
sw = File.AppendText("Data\\Log.txt");
sw.WriteLine(message);
sw.Close();
sw.Dispose();
}
}
}
Might be better to declare your logging class as static, and the locking object as #Adam Robinson suggested.
The question uses File.AppendText which is not an asynchronous method, and other answers correctly show that using a lock is the way to do it.
However, in many real-world cases, using an asynchronous method is preferred so the caller doesn't have to wait for this to get written. A lock isn't useful in that case as it blocks the thread and also async methods are not allowed inside the lock block.
In such situation, you could use Semaphores (SemaphoreSlim class in C#) to achieve the same thing, but with the bonus of being asynchronous and allowing asynchronous functions to be called inside the lock zone.
Here's a quick sample of using a SemaphoreSlim as an asynchronous lock:
// a semaphore as a private field in Logging class:
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
// Inside WriteToLog method:
try
{
await semaphore.WaitAsync();
// Code to write log to file asynchronously
}
finally
{
semaphore.Release();
}
Please note that it's good practice to always use semaphores in try..finally blocks, so even if the code throws an exception, the semaphore gets released correctly.
Here's what I'm trying to do:
Keep a queue in memory of items that need processed (i.e. IsProcessed = 0)
Every 5 seconds, get unprocessed items from the db, and if they're not already in the queue, add them
Continuous pull items from the queue, process them, and each time an item is processed, update it in the db (IsProcessed = 1)
Do this all "as parallel as possible"
I have a constructor for my service like
public MyService()
{
Ticker.Elapsed += FillQueue;
}
and I start that timer when the service starts like
protected override void OnStart(string[] args)
{
Ticker.Enabled = true;
Task.Run(() => { ConsumeWork(); });
}
and my FillQueue is like
private static async void FillQueue(object source, ElapsedEventArgs e)
{
var items = GetUnprocessedItemsFromDb();
foreach(var item in items)
{
if(!Work.Contains(item))
{
Work.Enqueue(item);
}
}
}
and my ConsumeWork is like
private static void ConsumeWork()
{
while(true)
{
if(Work.Count > 0)
{
var item = Work.Peek();
Process(item);
Work.Dequeue();
}
else
{
Thread.Sleep(500);
}
}
}
However this is probably a naive implementation and I'm wondering whether .NET has any type of class that is exactly what I need for this type of situation.
Though #JSteward' answer is a good start, you can improve it with mixing up the TPL-Dataflow and Rx.NET extensions, as a dataflow block may easily become an observer for your data, and with Rx Timer it will be much less effort for you (Rx.Timer explanation).
We can adjust MSDN article for your needs, like this:
private const int EventIntervalInSeconds = 5;
private const int DueIntervalInSeconds = 60;
var source =
// sequence of Int64 numbers, starting from 0
// https://msdn.microsoft.com/en-us/library/hh229435.aspx
Observable.Timer(
// fire first event after 1 minute waiting
TimeSpan.FromSeconds(DueIntervalInSeconds),
// fire all next events each 5 seconds
TimeSpan.FromSeconds(EventIntervalInSeconds))
// each number will have a timestamp
.Timestamp()
// each time we select some items to process
.SelectMany(GetItemsFromDB)
// filter already added
.Where(i => !_processedItemIds.Contains(i.Id));
var action = new ActionBlock<Item>(ProcessItem, new ExecutionDataflowBlockOptions
{
// we can start as many item processing as processor count
MaxDegreeOfParallelism = Environment.ProcessorCount,
});
IDisposable subscription = source.Subscribe(action.AsObserver());
Also, your check for item being already processed isn't quite accurate, as there is a possibility to item get selected as unprocessed from db right at the time you've finished it's processing, yet didn't update it in database. In this case item will be removed from Queue<T>, and after that added there again by producer, this is why I've added the ConcurrentBag<T> to this solution (HashSet<T> isn't thread-safe):
private static async Task ProcessItem(Item item)
{
if (_processedItemIds.Contains(item.Id))
{
return;
}
_processedItemIds.Add(item.Id);
// actual work here
// save item as processed in database
// we need to wait to ensure item not to appear in queue again
await Task.Delay(TimeSpan.FromSeconds(EventIntervalInSeconds * 2));
// clear the processed cache to reduce memory usage
_processedItemIds.Remove(item.Id);
}
public class Item
{
public Guid Id { get; set; }
}
// temporary cache for items in process
private static ConcurrentBag<Guid> _processedItemIds = new ConcurrentBag<Guid>();
private static IEnumerable<Item> GetItemsFromDB(Timestamped<long> time)
{
// log event timing
Console.WriteLine($"Event # {time.Value} at {time.Timestamp}");
// return items from DB
return new[] { new Item { Id = Guid.NewGuid() } };
}
You can implement cache clean up in other way, for example, start a "GC" timer, which will remove processed items from cache on regular basis.
To stop events and processing items you should Dispose the subscription and, maybe, Complete the ActionBlock:
subscription.Dispose();
action.Complete();
You can find more information about Rx.Net in their guidelines on github.
You could use an ActionBlock to do your processing, it has a built in queue that you can post work to. You can read up on tpl-dataflow here: Intro to TPL-Dataflow also Introduction to Dataflow, Part 1. Finally, this is a quick sample to get you going. I've left out a lot but it should at least get you started.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace MyWorkProcessor {
public class WorkProcessor {
public WorkProcessor() {
Processor = CreatePipeline();
}
public async Task StartProcessing() {
try {
await Task.Run(() => GetWorkFromDatabase());
} catch (OperationCanceledException) {
//handle cancel
}
}
private CancellationTokenSource cts {
get;
set;
}
private ITargetBlock<WorkItem> Processor {
get;
}
private TimeSpan DatabasePollingFrequency {
get;
} = TimeSpan.FromSeconds(5);
private ITargetBlock<WorkItem> CreatePipeline() {
var options = new ExecutionDataflowBlockOptions() {
BoundedCapacity = 100,
CancellationToken = cts.Token
};
return new ActionBlock<WorkItem>(item => ProcessWork(item), options);
}
private async Task GetWorkFromDatabase() {
while (!cts.IsCancellationRequested) {
var work = await GetWork();
await Processor.SendAsync(work);
await Task.Delay(DatabasePollingFrequency);
}
}
private async Task<WorkItem> GetWork() {
return await Context.GetWork();
}
private void ProcessWork(WorkItem item) {
//do processing
}
}
}
I use C# iterators as a replacement for coroutines, and it has been working great. I want to switch to async/await as I think the syntax is cleaner and it gives me type safety.
In this (outdated) blog post, Jon Skeet shows a possible way to implement it.
I chose to go a slightly different way (by implementing my own SynchronizationContext and using Task.Yield). This worked fine.
Then I realized there would be a problem; currently a coroutine doesn't have to finish running. It can be stopped gracefully at any point where it yields. We might have code like this:
private IEnumerator Sleep(int milliseconds)
{
Stopwatch timer = Stopwatch.StartNew();
do
{
yield return null;
}
while (timer.ElapsedMilliseconds < milliseconds);
}
private IEnumerator CoroutineMain()
{
try
{
// Do something that runs over several frames
yield return Coroutine.Sleep(5000);
}
finally
{
Log("Coroutine finished, either after 5 seconds, or because it was stopped");
}
}
The coroutine works by keeping track of all enumerators in a stack. The C# compiler generates a Dispose function which can be called to ensure that the 'finally' block is correctly invoked in CoroutineMain, even if the enumeration isn't finished. This way we can stop a coroutine gracefully, and still ensure finally blocks are invoked, by calling Dispose on all the IEnumerator objects on the stack. This is basically manually unwinding.
When I wrote my implementation with async/await I realized that we would lose this feature, unless I'm mistaken. I then looked up other coroutine solutions, and it doesn't look like Jon Skeet's version handles it in any way either.
The only way I can think of to handle this would be to have our own custom 'Yield' function, which would check if the coroutine was stopped, and then raise an exception that indicated this. This would propagate up, executing finally blocks, and then be caught somewhere near the root. I don't find this pretty though, as 3rd party code could potentially catch the exception.
Am I misunderstanding something, and is this possible to do in an easier way? Or do I need to go the exception way to do this?
EDIT: More information/code has been requested, so here's some. I can guarantee this is going to be running on only a single thread, so there's no threading involved here.
Our current coroutine implementation looks a bit like this (this is simplified, but it works in this simple case):
public sealed class Coroutine : IDisposable
{
private class RoutineState
{
public RoutineState(IEnumerator enumerator)
{
Enumerator = enumerator;
}
public IEnumerator Enumerator { get; private set; }
}
private readonly Stack<RoutineState> _enumStack = new Stack<RoutineState>();
public Coroutine(IEnumerator enumerator)
{
_enumStack.Push(new RoutineState(enumerator));
}
public bool IsDisposed { get; private set; }
public void Dispose()
{
if (IsDisposed)
return;
while (_enumStack.Count > 0)
{
DisposeEnumerator(_enumStack.Pop().Enumerator);
}
IsDisposed = true;
}
public bool Resume()
{
while (true)
{
RoutineState top = _enumStack.Peek();
bool movedNext;
try
{
movedNext = top.Enumerator.MoveNext();
}
catch (Exception ex)
{
// Handle exception thrown by coroutine
throw;
}
if (!movedNext)
{
// We finished this (sub-)routine, so remove it from the stack
_enumStack.Pop();
// Clean up..
DisposeEnumerator(top.Enumerator);
if (_enumStack.Count <= 0)
{
// This was the outer routine, so coroutine is finished.
return false;
}
// Go back and execute the parent.
continue;
}
// We executed a step in this coroutine. Check if a subroutine is supposed to run..
object value = top.Enumerator.Current;
IEnumerator newEnum = value as IEnumerator;
if (newEnum != null)
{
// Our current enumerator yielded a new enumerator, which is a subroutine.
// Push our new subroutine and run the first iteration immediately
RoutineState newState = new RoutineState(newEnum);
_enumStack.Push(newState);
continue;
}
// An actual result was yielded, so we've completed an iteration/step.
return true;
}
}
private static void DisposeEnumerator(IEnumerator enumerator)
{
IDisposable disposable = enumerator as IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
Assume we have code like the following:
private IEnumerator MoveToPlayer()
{
try
{
while (!AtPlayer())
{
yield return Sleep(500); // Move towards player twice every second
CalculatePosition();
}
}
finally
{
Log("MoveTo Finally");
}
}
private IEnumerator OrbLogic()
{
try
{
yield return MoveToPlayer();
yield return MakeExplosion();
}
finally
{
Log("OrbLogic Finally");
}
}
This would be created by passing an instance of the OrbLogic enumerator to a Coroutine, and then running it. This allows us to tick the coroutine every frame. If the player kills the orb, the coroutine doesn't finish running; Dispose is simply called on the coroutine. If MoveTo was logically in the 'try' block, then calling Dispose on the top IEnumerator will, semantically, make the finally block in MoveTo execute. Then afterwards the finally block in OrbLogic will execute.
Note that this is a simple case and the cases are much more complex.
I am struggling to implement similar behavior in the async/await version. The code for this version looks like this (error checking omitted):
public class Coroutine
{
private readonly CoroutineSynchronizationContext _syncContext = new CoroutineSynchronizationContext();
public Coroutine(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
_syncContext.Next = new CoroutineSynchronizationContext.Continuation(state => action(), null);
}
public bool IsFinished { get { return !_syncContext.Next.HasValue; } }
public void Tick()
{
if (IsFinished)
throw new InvalidOperationException("Cannot resume Coroutine that has finished");
SynchronizationContext curContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(_syncContext);
// Next is guaranteed to have value because of the IsFinished check
Debug.Assert(_syncContext.Next.HasValue);
// Invoke next continuation
var next = _syncContext.Next.Value;
_syncContext.Next = null;
next.Invoke();
}
finally
{
SynchronizationContext.SetSynchronizationContext(curContext);
}
}
}
public class CoroutineSynchronizationContext : SynchronizationContext
{
internal struct Continuation
{
public Continuation(SendOrPostCallback callback, object state)
{
Callback = callback;
State = state;
}
public SendOrPostCallback Callback;
public object State;
public void Invoke()
{
Callback(State);
}
}
internal Continuation? Next { get; set; }
public override void Post(SendOrPostCallback callback, object state)
{
if (callback == null)
throw new ArgumentNullException("callback");
if (Current != this)
throw new InvalidOperationException("Cannot Post to CoroutineSynchronizationContext from different thread!");
Next = new Continuation(callback, state);
}
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException();
}
public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{
throw new NotSupportedException();
}
public override SynchronizationContext CreateCopy()
{
throw new NotSupportedException();
}
}
I don't see how to implement similar behavior to the iterator version using this.
Apologies in advance for the lengthy code!
EDIT 2: The new method seems to be working. It allows me to do stuff like:
private static async Task Test()
{
// Second resume
await Sleep(1000);
// Unknown how many resumes
}
private static async Task Main()
{
// First resume
await Coroutine.Yield();
// Second resume
await Test();
}
Which provides a very nice way of building AI for games.
Updated, a follow-up blog post:
Asynchronous coroutines with C# 8.0 and IAsyncEnumerable.
I use C# iterators as a replacement for coroutines, and it has been
working great. I want to switch to async/await as I think the syntax
is cleaner and it gives me type safety...
IMO, it's a very interesting question, although it took me awhile to fully understand it. Perhaps, you didn't provide enough sample code to illustrate the concept. A complete app would help, so I'll try to fill this gap first. The following code illustrates the usage pattern as I understood it, please correct me if I'm wrong:
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
// https://stackoverflow.com/q/22852251/1768303
public class Program
{
class Resource : IDisposable
{
public void Dispose()
{
Console.WriteLine("Resource.Dispose");
}
~Resource()
{
Console.WriteLine("~Resource");
}
}
private IEnumerator Sleep(int milliseconds)
{
using (var resource = new Resource())
{
Stopwatch timer = Stopwatch.StartNew();
do
{
yield return null;
}
while (timer.ElapsedMilliseconds < milliseconds);
}
}
void EnumeratorTest()
{
var enumerator = Sleep(100);
enumerator.MoveNext();
Thread.Sleep(500);
//while (e.MoveNext());
((IDisposable)enumerator).Dispose();
}
public static void Main(string[] args)
{
new Program().EnumeratorTest();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
}
}
Here, Resource.Dispose gets called because of ((IDisposable)enumerator).Dispose(). If we don't call enumerator.Dispose(), then we'll have to uncomment //while (e.MoveNext()); and let the iterator finish gracefully, for proper unwinding.
Now, I think the best way to implement this with async/await is to use a custom awaiter:
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
// https://stackoverflow.com/q/22852251/1768303
public class Program
{
class Resource : IDisposable
{
public void Dispose()
{
Console.WriteLine("Resource.Dispose");
}
~Resource()
{
Console.WriteLine("~Resource");
}
}
async Task SleepAsync(int milliseconds, Awaiter awaiter)
{
using (var resource = new Resource())
{
Stopwatch timer = Stopwatch.StartNew();
do
{
await awaiter;
}
while (timer.ElapsedMilliseconds < milliseconds);
}
Console.WriteLine("Exit SleepAsync");
}
void AwaiterTest()
{
var awaiter = new Awaiter();
var task = SleepAsync(100, awaiter);
awaiter.MoveNext();
Thread.Sleep(500);
//while (awaiter.MoveNext()) ;
awaiter.Dispose();
task.Dispose();
}
public static void Main(string[] args)
{
new Program().AwaiterTest();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
// custom awaiter
public class Awaiter :
System.Runtime.CompilerServices.INotifyCompletion,
IDisposable
{
Action _continuation;
readonly CancellationTokenSource _cts = new CancellationTokenSource();
public Awaiter()
{
Console.WriteLine("Awaiter()");
}
~Awaiter()
{
Console.WriteLine("~Awaiter()");
}
public void Cancel()
{
_cts.Cancel();
}
// let the client observe cancellation
public CancellationToken Token { get { return _cts.Token; } }
// resume after await, called upon external event
public bool MoveNext()
{
if (_continuation == null)
return false;
var continuation = _continuation;
_continuation = null;
continuation();
return _continuation != null;
}
// custom Awaiter methods
public Awaiter GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get { return false; }
}
public void GetResult()
{
this.Token.ThrowIfCancellationRequested();
}
// INotifyCompletion
public void OnCompleted(Action continuation)
{
_continuation = continuation;
}
// IDispose
public void Dispose()
{
Console.WriteLine("Awaiter.Dispose()");
if (_continuation != null)
{
Cancel();
MoveNext();
}
}
}
}
}
When it's time to unwind, I request the cancellation inside Awaiter.Dispose and drive the state machine to the next step (if there's a pending continuation). This leads to observing the cancellation inside Awaiter.GetResult (which is called by the compiler-generated code). That throws TaskCanceledException and further unwinds the using statement. So, the Resource gets properly disposed of. Finally, the task transitions to the cancelled state (task.IsCancelled == true).
IMO, this is a more simple and direct approach than installing a custom synchronization context on the current thread. It can be easily adapted for multithreading (some more details here).
This should indeed give you more freedom than with IEnumerator/yield. You could use try/catch inside your coroutine logic, and you can observe exceptions, cancellation and the result directly via the Task object.
Updated, AFAIK there is no analogy for the iterator's generated IDispose, when it comes to async state machine. You really have to drive the state machine to an end when you want to cancel/unwind it. If you want to account for some negligent use of try/catch preventing the cancellation, I think the best you could do is to check if _continuation is non-null inside Awaiter.Cancel (after MoveNext) and throw a fatal exception out-of-the-band (using a helper async void method).
Updated, this has evolved to a blog post:
Asynchronous coroutines with C# 8.0 and IAsyncEnumerable.
It's 2020 and my other answer about await and coroutines is quite outdated by today's C# language standards. C# 8.0 has introduced support for asynchronous streams with new features like:
IAsyncEnumerable
IAsyncEnumerator
await foreach
IAsyncDisposable
await using
To familiarize yourself with the concept of asynchronous streams, I could highly recommend reading "Iterating with Async Enumerables in C# 8", by Stephen Toub.
Together, these new features provide a great base for implementing asynchronous co-routines in C# in a much more natural way.
Wikipedia provides a good explanation of what co-routines (aka corotines) generally are. What I'd like to show here is how co-routines can be async, suspending their execution flow by using await and arbitrary swapping the roles of being producer/consumer to each other, with C# 8.0.
The code fragment below should illustrate the concept. Here we have two co-routines, CoroutineA and CoroutineB which execute cooperatively and asynchronously, by yielding to each other as their pseudo-linear execution flow goes on.
namespace Tests
{
[TestClass]
public class CoroutineProxyTest
{
const string TRACE_CATEGORY = "coroutines";
/// <summary>
/// CoroutineA yields to CoroutineB
/// </summary>
private async IAsyncEnumerable<string> CoroutineA(
ICoroutineProxy<string> coroutineProxy,
[EnumeratorCancellation] CancellationToken token)
{
await using (var coroutine = await coroutineProxy.AsAsyncEnumerator(token))
{
const string name = "A";
var i = 0;
// yielding 1
Trace.WriteLine($"{name} about to yeild: {++i}", TRACE_CATEGORY);
yield return $"{i} from {name}";
// receiving
if (!await coroutine.MoveNextAsync())
{
yield break;
}
Trace.WriteLine($"{name} received: {coroutine.Current}", TRACE_CATEGORY);
// yielding 2
Trace.WriteLine($"{name} about to yeild: {++i}", TRACE_CATEGORY);
yield return $"{i} from {name}";
// receiving
if (!await coroutine.MoveNextAsync())
{
yield break;
}
Trace.WriteLine($"{name} received: {coroutine.Current}", TRACE_CATEGORY);
// yielding 3
Trace.WriteLine($"{name} about to yeild: {++i}", TRACE_CATEGORY);
yield return $"{i} from {name}";
}
}
/// <summary>
/// CoroutineB yields to CoroutineA
/// </summary>
private async IAsyncEnumerable<string> CoroutineB(
ICoroutineProxy<string> coroutineProxy,
[EnumeratorCancellation] CancellationToken token)
{
await using (var coroutine = await coroutineProxy.AsAsyncEnumerator(token))
{
const string name = "B";
var i = 0;
// receiving
if (!await coroutine.MoveNextAsync())
{
yield break;
}
Trace.WriteLine($"{name} received: {coroutine.Current}", TRACE_CATEGORY);
// yielding 1
Trace.WriteLine($"{name} about to yeild: {++i}", TRACE_CATEGORY);
yield return $"{i} from {name}";
// receiving
if (!await coroutine.MoveNextAsync())
{
yield break;
}
Trace.WriteLine($"{name} received: {coroutine.Current}", TRACE_CATEGORY);
// yielding 2
Trace.WriteLine($"{name} about to yeild: {++i}", TRACE_CATEGORY);
yield return $"{i} from {name}";
// receiving
if (!await coroutine.MoveNextAsync())
{
yield break;
}
Trace.WriteLine($"{name} received: {coroutine.Current}", TRACE_CATEGORY);
}
}
/// <summary>
/// Testing CoroutineA and CoroutineB cooperative execution
/// </summary>
[TestMethod]
public async Task Test_Coroutine_Execution_Flow()
{
// Here we execute two cotoutines, CoroutineA and CoroutineB,
// which asynchronously yield to each other
//TODO: test cancellation scenarios
var token = CancellationToken.None;
using (var apartment = new Tests.ThreadPoolApartment())
{
await apartment.Run(async () =>
{
var proxyA = new CoroutineProxy<string>();
var proxyB = new CoroutineProxy<string>();
var listener = new Tests.CategoryTraceListener(TRACE_CATEGORY);
Trace.Listeners.Add(listener);
try
{
// start both coroutines
await Task.WhenAll(
proxyA.Run(token => CoroutineA(proxyB, token), token),
proxyB.Run(token => CoroutineB(proxyA, token), token))
.WithAggregatedExceptions();
}
finally
{
Trace.Listeners.Remove(listener);
}
var traces = listener.ToArray();
Assert.AreEqual(traces[0], "A about to yeild: 1");
Assert.AreEqual(traces[1], "B received: 1 from A");
Assert.AreEqual(traces[2], "B about to yeild: 1");
Assert.AreEqual(traces[3], "A received: 1 from B");
Assert.AreEqual(traces[4], "A about to yeild: 2");
Assert.AreEqual(traces[5], "B received: 2 from A");
Assert.AreEqual(traces[6], "B about to yeild: 2");
Assert.AreEqual(traces[7], "A received: 2 from B");
Assert.AreEqual(traces[8], "A about to yeild: 3");
Assert.AreEqual(traces[9], "B received: 3 from A");
});
}
}
}
}
The test's output looks like this:
coroutines: A about to yeild: 1
coroutines: B received: 1 from A
coroutines: B about to yeild: 1
coroutines: A received: 1 from B
coroutines: A about to yeild: 2
coroutines: B received: 2 from A
coroutines: B about to yeild: 2
coroutines: A received: 2 from B
coroutines: A about to yeild: 3
coroutines: B received: 3 from A
I currently use asynchronous co-routines in some of my automated UI testing scenarios. E.g., I might have an asynchronous test workflow logic that runs on a UI thread (that'd be CouroutineA) and a complimentary workflow that runs on a ThreadPool thread as a part of a [TestMethod] method (that'd be CouroutineB).
Then I could do something like await WaitForUserInputAsync(); yield return true; to synchronize at certain key points of CouroutineA and CouroutineB cooperative execution flow.
Without yield return I'd have to use some form of asynchronous synchronization primitives, like Stephen Toub's AsyncManualResetEvent. I personally feel using co-routines is a more natural way of doing such kind of synchronization.
The code for CoroutineProxy (which drives the execution of co-routines) is still a work-in-progress. It currently uses TPL Dataflow's BufferBlock as a proxy queue to coordinate the asynchronous execution, and I am not sure yet if it is an optimal way of doing that. Currently, this is what it looks like this:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
#nullable enable
namespace Tests
{
public interface ICoroutineProxy<T>
{
public Task<IAsyncEnumerable<T>> AsAsyncEnumerable(CancellationToken token = default);
}
public static class CoroutineProxyExt
{
public async static Task<IAsyncEnumerator<T>> AsAsyncEnumerator<T>(
this ICoroutineProxy<T> #this,
CancellationToken token = default)
{
return (await #this.AsAsyncEnumerable(token)).GetAsyncEnumerator(token);
}
}
public class CoroutineProxy<T> : ICoroutineProxy<T>
{
readonly TaskCompletionSource<IAsyncEnumerable<T>> _proxyTcs =
new TaskCompletionSource<IAsyncEnumerable<T>>(TaskCreationOptions.RunContinuationsAsynchronously);
public CoroutineProxy()
{
}
private async IAsyncEnumerable<T> CreateProxyAsyncEnumerable(
ISourceBlock<T> bufferBlock,
[EnumeratorCancellation] CancellationToken token)
{
var completionTask = bufferBlock.Completion;
while (true)
{
var itemTask = bufferBlock.ReceiveAsync(token);
var any = await Task.WhenAny(itemTask, completionTask);
if (any == completionTask)
{
// observe completion exceptions if any
await completionTask;
yield break;
}
yield return await itemTask;
}
}
async Task<IAsyncEnumerable<T>> ICoroutineProxy<T>.AsAsyncEnumerable(CancellationToken token)
{
using (token.Register(() => _proxyTcs.TrySetCanceled(), useSynchronizationContext: true))
{
return await _proxyTcs.Task;
}
}
public async Task Run(Func<CancellationToken, IAsyncEnumerable<T>> routine, CancellationToken token)
{
token.ThrowIfCancellationRequested();
var bufferBlock = new BufferBlock<T>();
var proxy = CreateProxyAsyncEnumerable(bufferBlock, token);
_proxyTcs.SetResult(proxy); // throw if already set
try
{
//TODO: do we need to use routine(token).WithCancellation(token) ?
await foreach (var item in routine(token))
{
await bufferBlock.SendAsync(item, token);
}
bufferBlock.Complete();
}
catch (Exception ex)
{
((IDataflowBlock)bufferBlock).Fault(ex);
throw;
}
}
}
}
NOTE: I'm not saying this is a good idea, just trying to find out if there's a 'better' option than this brute-force one.
This came up in a previous SO thread # How to get the current task reference?
However, that thread was a bit more constrained by a particular interface.
The brute-force approach I threw together quickly just uses a dictionary of weak references.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace GetCurrentTaskExample
{
public static class TaskContext
{
// don't need a ConcurrentDictionary since we won't be reading/writing the same task id with different tasks concurrently
private static readonly Dictionary<int, WeakReference> s_indexedTaskReferences = new Dictionary<int, WeakReference>();
public static void AddAndStartTasks(IEnumerable<Task> tasks)
{
foreach (var task in tasks)
{
AddTask(task);
task.Start();
}
}
public static void AddTask(Task task)
{
s_indexedTaskReferences[task.Id] = new WeakReference(task);
}
public static Task GetCurrentTask()
{
var taskId = Task.CurrentId;
if (taskId == null) return null;
WeakReference weakReference;
if (s_indexedTaskReferences.TryGetValue(taskId.Value, out weakReference) == false) return null;
if (weakReference == null) return null; // should not happen since we don't store null as a value
var task = weakReference.Target as Task;
return task;
}
}
class Program
{
static void Main(string[] args)
{
var tasks = Enumerable.Range(0, 100)
.Select(i => new Task(VerifyCurrentTaskWorks, i))
.ToArray();
TaskContext.AddAndStartTasks(tasks);
Task.WaitAll(tasks);
}
static void VerifyCurrentTaskWorks(object instanceIdentifier)
{
var currentTask = TaskContext.GetCurrentTask();
if (currentTask.Id == Task.CurrentId)
{
Console.WriteLine("Verified for instance {0} that Task.CurrentId value of {1} matches Id property {2} of task {3}",
instanceIdentifier, Task.CurrentId, currentTask.Id, currentTask);
}
else
{
var errorMessage = String.Format("TaskContext.GetCurrentTask() failed for instance {0} with Task.CurrentId value of {1} and currentTask.Id value of {2}",
instanceIdentifier, Task.CurrentId, currentTask.Id);
throw new InvalidOperationException(errorMessage);
}
}
}
}
However, this clearly means whatever is creating the tasks is forced to deal with this additional headache, so it's not very usable, especially WRT C#5 async methods where the task isn't so explicitly created.
Again, probably a bad idea to have code that needs this, so consider it more akin to a thought exercise. :)
There's not much of a better way, unfortunately, and since the eventual goal was to key off of Task.CurrentId, it's indeed not even useful since we won't have an ability to get 'current task id' for async methods (after the first await, so it's returned to the caller already).