Combine/merge an unknown number of observables together as they are created - c#

What I would like to do is:
Call a function (DoWork) which as part of it's work will subscribe to multiple hot inputs through multiple Worker classes
Before calling the function subscribe to all the updates that DoWork subscribes to
Once finished, dispose of all the subscriptions
It is probable that at least one incoming event will fire before DoWork has completed.
Questions:
Are Subjects the correct way to do this? It feels like there should be a better way?
How to ensure that once the subscription in Main is disposed, all of the incomingX subscriptions are also disposed - i.e. Main should control the lifecycle of all the subscriptions.
void Main()
{
var worker = new Worker();
using (worker.UpdateEvents.Subscribe(x => Console.WriteLine()))
{
worker.DoWork();
}
}
public class Worker1
{
private readonly Subject<string> updateEvents = new Subject<string>();
public IObservable<string> UpdateEvents { get { return updateEvents; } }
public void DoWork()
{
// Do some work
// subscribe to a hot observable (events coming in over the network)
incoming1.Subscribe(updateEvents);
var worker2 = new Worker2();
worker2.UpdateEvents.Subscribe(updateEvents);
worker2.DoWork();
}
}
public class Worker2
{
private readonly Subject<string> updateEvents = new Subject<string>();
public IObservable<string> UpdateEvents { get { return updateEvents; } }
public void DoWork()
{
// Do some work
// subscribe to some more events
incoming2.Subscribe(updateEvents);
var workerN = new WorkerN();
workerN.UpdateEvents.Subscribe(updateEvents);
workerN.DoWork();
}
}

James's answer (use Subject and Merge) captures the essence of the question. This answer offers a pattern that I've found useful in this situation (based on your comments to James's answer).
Essentially the pattern is to have your worker's expose an IObservable that the caller will subscribe to before calling DoWork. But this sort of API (call A before calling B) is problematic because it introduces temporal coupling.
To eliminate the temporal coupling, you end up turning your worker itself into an cold Observable that implicitly calls DoWork when the caller subscribes. Once you realize the power of cold observables and the ability to use Observable.Create to take action when an observer subscribes, the sky's the limit on the Rx chains you can create without ever needing to reach for a Subject. Here's an example based on your original code.
Worker is simple. It just subscribes to incoming1 and to Worker2.
Worker2 is slightly more complex. It subscribes to incoming2, performs some additional work, then finally subscribes to WorkerN.
All the while maintaining the correct OnError, OnCompleted logic which your original code example fails to do. Meaning that the observable stream that Main sees does not Complete until all of the incoming streams and work streams complete. But Main fails as soon as any of the incoming streams or work streams fails. Your code example with multiple calls to Subscribe(someSubject) would cause the Subject to complete (and thus Main's incoming stream to complete) as soon as any of the incoming streams completes.
public class Worker1
{
public IObservable<string> UpdateEvents { get; private set; };
public Worker1()
{
// Each time someone subscribes, create a new worker2 and subscribe to the hot events as well as whatever worker2 produces.
UpdateEvents = Observable.Create(observer =>
{
var worker2 = new Worker2();
return incoming1.Merge(worker2.UpdateEvents).Subscribe(observer);
});
}
}
public class Worker2
{
public IObservable<string> UpdateEvents { get; private set; };
public Worker2()
{
// Each time someone subscribes, create a new worker and subscribe to the hot events as well as whatever worker2 produces.
UpdateEvents = Observable.Create(observer =>
{
// maybe this version needs to do some stuff after it has subscribed to incoming2 but before it subscribes to workerN:
var doWorkThenSubscribeToWorker = Observable.Create(o =>
{
DoWork(o);
var worker = new WorkerN();
return worker.UpdateEvents.Subscribe(o);
}
return incoming2.Merge(doWorkThenSubscribeToWorker).Subscribe(observer);
});
}
private void DoWork(IObserver<string> observer)
{
// do some work
observer.OnNext("result of work");
}
}
void Main()
{
var worker = new Worker();
worker.UpdateEvents.Do(x => Console.WriteLine()).Wait();
}

It's hard to follow exactly what you are asking for - a small but complete program would help here I think.
That said, there's nothing wrong with using a Subject to introduce input into the Rx pipeline - there's a lot written about that on StackOverflow and elsewhere, so I won't rehash it.
Going just by the title of your question, I wonder does the following suit your purpose?
Combining a dynamic number of streams
To do this you can use Merge on a stream of streams. Your streams must all be of the same type - if they are not, you can create a suitable container type and project them into that type using Select. For simplicity, I will assume the unified type is long.
To start create a container for the streams:
var container = new Subject<IObservable<long>>();
Then combine the contained streams:
var combined = container.Merge();
Subscribe to combined to consume the results in the usual way, and dispose the subscription to unsubscribe from all streams at once.
You can then add streams as they are created like this:
// assume we got this from somewhere - e.g. a "worker" factory function
// Observable.Create may well be helpful to create an observable
// that initiates getting data from a network connection upon its subscription
IObservable<long> someNewStream;
// add the someNewStream to the container (it will be subscribed to once added)
container.OnNext(someNewStream);
Example use
// dump out the combined results to the console,
// IRL you would subscribe to this to process the results
var subscription = combined.Subscribe(Console.WriteLine);
// add a stream of longs
container.OnNext(Observable.Interval(TimeSpan.FromSeconds(1)));
Console.WriteLine("Stream 1 added");
Console.ReadLine();
// add another stream
container.OnNext(Observable.Interval(TimeSpan.FromSeconds(1)));
Console.WriteLine("Step 2");
Console.ReadLine();
// this will unsubscribe from all the live streams
subscription.Dispose();

Related

How do I prevent by Rx test from hanging?

I am reproducing my Rx issue with a simplified test case below. The test below hangs. I am sure it is a small, but fundamental, thing that I am missing, but can't put my finger on it.
public class Service
{
private ISubject<double> _subject = new Subject<double>();
public void Reset()
{
_subject.OnNext(0.0);
}
public IObservable<double> GetProgress()
{
return _subject;
}
}
public class ObTest
{
[Fact]
private async Task SimpleTest()
{
var service = new Service();
var result = service.GetProgress().Take(1);
var task = Task.Run(async () =>
{
service.Reset();
});
await result;
}
}
UPDATE
My attempt above was to simplify the problem a little and understand it. In my case GetProgress() is a merge of various Observables that publish the download progress, one of these Observables is a Subject<double> that publishes 0 everytime somebody calls a method to delete the download.
The race condition identified by Enigmativity and Theodor Zoulias may(??) happen in real life. I display a view which attempts to get the progress, however, quick fingers delete it just in time.
What I need to understand a bit more is if the download is started again (subscription has taken place by now, by virtue of displaying a view, which has already made the subscription) and somebody again deletes it.
public class Service
{
private ISubject<double> _deleteSubject = new Subject<double>();
public void Reset()
{
_deleteSubject.OnNext(0.0);
}
public IObservable<double> GetProgress()
{
return _deleteSubject.Merge(downloadProgress);
}
}
Your code isn't hanging. It's awaiting an observable that sometimes never gets a value.
You have a race condition.
The Task.Run is sometimes executing to completion before the await result creates the subscription to the observable - so it never sees the value.
Try this code instead:
private async Task SimpleTest()
{
var service = new Service();
var result = service.GetProgress().Take(1);
var awaiter = result.GetAwaiter();
var task = Task.Run(() =>
{
service.Reset();
});
await awaiter;
}
The line await result creates a subscription to the observable. The problem is that the notification _subject.OnNext(0.0) may occur before this subscription, in which case the value will pass unobserved, and the await result will continue waiting for a notification for ever. In this particular example the notification is always missed, at least in my PC, because the subscription is delayed for around 30 msec (measured with a Stopwatch), which is longer than the time needed for the task that resets the service to complete, probably because the JITer must load and compile some RX-related assembly. The situation changes when I do a warm-up by calling new Subject<int>().FirstAsync().Subscribe() before running the example. In that case the notification is observed almost always, and the hanging is avoided.
I can think of two robust solutions to this problem.
The solution suggested by Enigmativity, to create an awaitable subscription before starting the task that resets the service. This can be done with either GetAwaiter or ToTask.
To use a ReplaySubject<T> instead of a plain vanilla Subject<T>.
Represents an object that is both an observable sequence as well as an observer. Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
The ReplaySubject will cache the value so that it can be observed by the future subscription, eliminating the race condition. You could initialize it with a bufferSize of 1 to minimize the memory footprint of the buffer.

Enforce Observable Subscribers to only write to the stream one at a time

I currently am using observables to manage messages being generated on bus which are being pushed over various streams.
All works well but as messages can come in, it's possible for the system to try and write multiple messages to the stream at once (i.e. messages coming in from multiple threads) or that messages are published quicker than they can be written to the stream... as you can image, this causes issues when writing.
Hence I'm trying to figure out how I can organize things so that when messages come in only one will be processed at a time. Any thoughts?
public class MessageStreamResource : IResourceStartup
{
private readonly IBus _bus;
private readonly ISubject<string> _sender;
public MessageStreamResource(IBus bus)
{
_bus = bus;
_senderSubject = new Subject<string>();
//`All` can publish messages at the same time as it's
//collecting data being generated from different threads
_bus.All.Subscribe(message => Observable.Start(() => ProcessMessage(message), TaskPoolScheduler.Default));
//Note the above hops off the calls context so that the
//writing to the stream wont slow down the caller.
}
public void Configure(IAppBuilder app)
{
app.Map("/stream", async context =>
{
...
await context.Response.WriteAsync("Lets party!\n");
await context.Response.Body.FlushAsync();
var unSubscribe = _sender.Subscribe(async t =>
{
//PROBLEM HERE
//I only want this callback to be executed
//one at a time...
await context.Response.WriteAsync($"{t}\n");
await context.Response.Body.FlushAsync();
});
...
await HoldOpenTask;
});
}
private void ProcessMessage(IMessage message)
{
_sender.OnNext(message.Payload);
}
}
If I understood the question correctly, this possibly can be done with SemaphoreSlim:
// ...
var semaphore = new SemaphoreSlim(initialCount: 1);
var unSubscribe = _sender.Subscribe(async t =>
{
//PROBLEM HERE
//I only want this callback to be executed
//one at a time...
await semaphore.WaitAsync();
try
{
await context.Response.WriteAsync($"{t}\n");
await context.Response.Body.FlushAsync();
}
finally
{
semaphore.Release();
}
});
SemaphoreSlim is IDisposable, make sure to dispose of it when appropriate.
Updated, from the second look, MapExtensions.Map
accepts Action<IAppBuilder>, so you're passing an async void lambda, essentially creating a bunch of fire-and-forget asynchronous operations. The Map call will return to the caller, while they may still be lingering around. This is most likely not what you want, is it?

Non blocking and reoccurring producer/consumer notifier implementation

Searched hard for a piece of code which does what i want and i am happy with. Reading this and this helped a lot.
I have a scenario where i need a single consumer to be notified by a single producer when new data is available but would also like the consumer to be notified periodically regardless of if new data is available.
It is fine if the consumer is notified more than the reoccurring period but it should not be notified less frequent.
It is possible that multiple notifications for 'new data' occur while the consumer is already notified and working. (So SemaphoreSlim was not a good fit).
Hence, a consumer which is slower than the rate of producer notifications, would not queue up subsequent notifications, they would just "re-signal" that same "data available" flag without affect.
I would also like the consumer to asynchronously wait for the notifications (without blocking a thread).
I have stitched together the below class which wraps around TaskCompletionSource and also uses an internal Timer.
public class PeriodicalNotifier : IDisposable
{
// Need some dummy type since TaskCompletionSource has only the generic version
internal struct VoidTypeStruct { }
// Always reuse this allocation
private static VoidTypeStruct dummyStruct;
private TaskCompletionSource<VoidTypeStruct> internalCompletionSource;
private Timer reSendTimer;
public PeriodicalNotifier(int autoNotifyIntervalMs)
{
internalCompletionSource = new TaskCompletionSource<VoidTypeStruct>();
reSendTimer = new Timer(_ => Notify(), null, 0, autoNotifyIntervalMs);
}
public async Task WaitForNotifictionAsync(CancellationToken cancellationToken)
{
using (cancellationToken.Register(() => internalCompletionSource.TrySetCanceled()))
{
await internalCompletionSource.Task;
// Recreate - to be able to set again upon the next wait
internalCompletionSource = new TaskCompletionSource<VoidTypeStruct>();
}
}
public void Notify()
{
internalCompletionSource.TrySetResult(dummyStruct);
}
public void Dispose()
{
reSendTimer.Dispose();
internalCompletionSource.TrySetCanceled();
}
}
Users of this class can do something like this:
private PeriodicalNotifier notifier = new PeriodicalNotifier(100);
// ... In some task - which should be non-blocking
while (some condition)
{
await notifier.WaitForNotifictionAsync(_tokenSource.Token);
// Do some work...
}
// ... In some thread, producer added new data
notifier.Notify();
Efficiency is important to me, the scenario is of a high frequency data stream, and so i had in mind:
The non-blocking nature of the wait.
I assume Timer is more efficient than recreating Task.Delay and cancelling it if it's not the one to notify.
A concern for the recreation of the TaskCompletionSource
My questions are:
Does my code correctly solve the problem? Any hidden pitfalls?
Am i missing some trivial solution / existing block for this use case?
Update:
I have reached a conclusion that aside from re implementing a more lean Task Completion structure (like in here and here) i have no more optimizations to make. Hope that helps anyone looking at a similar scenario.
Yes, your implementation makes sense but the TaskCompletionSource recreation should be outside the using scope, otherwise the "old" cancellation token may cancel the "new" TaskCompletionSource.
I think using some kind of AsyncManualResetEvent combined with a Timer would be simpler and less error-prone. There's a very nice namespace with async tools in the Visual Studio SDK by Microsoft. You need to install the SDK and then reference the Microsoft.VisualStudio.Threading assembly. Here's an implementation using their AsyncManualResetEvent with the same API:
public class PeriodicalNotifier : IDisposable
{
private readonly Timer _timer;
private readonly AsyncManualResetEvent _asyncManualResetEvent;
public PeriodicalNotifier(TimeSpan autoNotifyInterval)
{
_asyncManualResetEvent = new AsyncManualResetEvent();
_timer = new Timer(_ => Notify(), null, TimeSpan.Zero, autoNotifyInterval);
}
public async Task WaitForNotifictionAsync(CancellationToken cancellationToken)
{
await _asyncManualResetEvent.WaitAsync().WithCancellation(cancellationToken);
_asyncManualResetEvent.Reset();
}
public void Notify()
{
_asyncManualResetEvent.Set();
}
public void Dispose()
{
_timer.Dispose();
}
}
You notify by setting the reset event, asynchronously wait using WaitAsync, enable Cancellation using the WithCancellation extension method and then reset the event. Multiple notifications are "merged" by setting the same reset event.
Subject<Result> notifier = new Subject<Result)();
notifier
.Select(value => Observable.Interval(TimeSpan.FromMilliSeconds(100))
.Select(_ => value)).Switch()
.Subscribe(value => DoSomething(value));
//Some other thread...
notifier.OnNext(...);
This Rx query will keep sending value, every 100 milliseconds, until a new value turns up. Then we notify that value every 100 milliseconds.
If we receive values faster than once every 100 milliseconds, then we basically have the same output as input.

BlockingCollection vs Subject for use as a consumer

I'm trying to implement a consumer in C#. There are many publishers which could be executing concurrently. I've created three examples, one with Rx and subject, one with BlockingCollection and a third using ToObservable from the BlockingCollection. They all do the same thing in this simple example and I want them to work with multiple producers.
What are the different qualities of each approach?
I'm already using Rx, so I'd prefer this approach. But I'm concerned that OnNext has no thread safe guarantee and I don't know what the queuing semantics are of Subject and the default scheduler.
Is there a thread safe subject?
Are all messages going to be processed?
Are there any other scenarios when this wont work? Is it processing concurrently?
void SubjectOnDefaultScheduler()
{
var observable = new Subject<long>();
observable.
ObserveOn(Scheduler.Default).
Subscribe(i => { DoWork(i); });
observable.OnNext(1);
observable.OnNext(2);
observable.OnNext(3);
}
Not Rx, but easily adapted to use/subscribe it. It takes an item and then processes it. This should happen serially.
void BlockingCollectionAndConsumingTask()
{
var blockingCollection = new BlockingCollection<long>();
var taskFactory = new TaskFactory();
taskFactory.StartNew(() =>
{
foreach (var i in blockingCollection.GetConsumingEnumerable())
{
DoWork(i);
}
});
blockingCollection.Add(1);
blockingCollection.Add(2);
blockingCollection.Add(3);
}
Using a blocking collection a bit like a subject seems like a good compromise. I'm guessing implicitly will schedule onto task, so that I can use async/await, is that correct?
void BlockingCollectionToObservable()
{
var blockingCollection = new BlockingCollection<long>();
blockingCollection.
GetConsumingEnumerable().
ToObservable(Scheduler.Default).
Subscribe(i => { DoWork(i); });
blockingCollection.Add(1);
blockingCollection.Add(2);
blockingCollection.Add(3);
}
Subject is not thread-safe. OnNexts issued concurrently will directly call an Observer concurrently. Personally I find this quite surprising given the extent to which other areas of Rx enforce the correct semantics. I can only assume this was done for performance considerations.
Subject is kind of a half-way house though, in that it does enforce termination with OnError or OnComplete - after either of these are raised, OnNext is a NOP. And this behaviour is thread-safe.
But use Observable.Synchronize() on a Subject and it will force outgoing calls to obey the proper Rx semantics. In particular, OnNext calls will block if made concurrently.
The underlying mechanism is the standard .NET lock. When the lock is contended by multiple threads they are granted the lock on a first-come first-served basis most of the time. There are certain conditions where fairness is violated. However, you will definitely get the serialized access you are looking for.
ObserveOn has behaviour that is platform specific - if available, you can supply a SynchronizationContext and OnNext calls are Posted to it. With a Scheduler, it ends up putting calls onto a ConcurrentQueue<T> and dispatching them serially via the scheduler - so the thread of execution will depend on the scheduler. Either way, the queuing behaviour will also enforce the correct semantics.
In both cases (Synchronize & ObserveOn), you certainly won't lose messages. With ObserveOn, you can implicitly choose thread you'll process messages on by your choice of Scheduler/Context, with Synchronize you'll process messages on the calling thread. Which is better will depend on your scenario.
There's more to consider as well - such as what you want to do if your producers out-pace your consumer.
You might want to have a look at Rxx Consume as well: http://rxx.codeplex.com/SourceControl/changeset/view/63470#1100703
Sample code showing Synchronize behaviour (Nuget Rx-Testing, Nunit) - it's a bit hokey with the Thread.Sleep code but it's quite fiddly to be bad and I was lazy :):
public class SubjectTests
{
[Test]
public void SubjectDoesNotRespectGrammar()
{
var subject = new Subject<int>();
var spy = new ObserverSpy(Scheduler.Default);
var sut = subject.Subscribe(spy);
// Swap the following with the preceding to make this test pass
//var sut = subject.Synchronize().Subscribe(spy);
Task.Factory.StartNew(() => subject.OnNext(1));
Task.Factory.StartNew(() => subject.OnNext(2));
Thread.Sleep(2000);
Assert.IsFalse(spy.ConcurrencyViolation);
}
private class ObserverSpy : IObserver<int>
{
private int _inOnNext;
public ObserverSpy(IScheduler scheduler)
{
_scheduler = scheduler;
}
public bool ConcurrencyViolation = false;
private readonly IScheduler _scheduler;
public void OnNext(int value)
{
var isInOnNext = Interlocked.CompareExchange(ref _inOnNext, 1, 0);
if (isInOnNext == 1)
{
ConcurrencyViolation = true;
return;
}
var wait = new ManualResetEvent(false);
_scheduler.Schedule(TimeSpan.FromSeconds(1), () => wait.Set());
wait.WaitOne();
_inOnNext = 0;
}
public void OnError(Exception error)
{
}
public void OnCompleted()
{
}
}
}

how to propagate some data to main process from TPL tasks while tasks are running

I have a situation where I create a list of long running tasks which monitors some system/network resources and then sends email, logs into a txt file, and calls a web service when some conditions are met. Then begins monitoring again. These tasks are created in a windows service and hence will be running all the time.
I want them to raise events or something to notify the parent class (which created them) and it will performs the 3 operations i mentioned above instead of each object in tasks doing it by itself.
And how can it be controlled that only a single task uses that parent class's method at a single time. As Email and a web service call is involved, so two concurrent requests may beak the code.
UPDATE
These Watchers are of three types, each implements the following interface.
public interface IWatcher
{
void BeginWatch();
}
Classes that implement are
//this watcher is responsible for watching over a sql query result
public class DBWatcher : IWatcher
{
....
void BeginWatch()
{
//Here a timer is created which contiously checks the SQL query result.
//And would Call SERVICE, send an EMAIL and LOG into a file
Timer watchIterator = new Timer(this._intervalMinutes * 60000);
watchIterator.Elapsed += new ElapsedEventHandler(_watchIterator_Elapsed);
watchIterator.Start();
}
void _watchIterator_Elapsed(object sender, ElapsedEventArgs e)
{
//1. Check Query result
//3. Call SERVICE, send an EMAIL and LOG into a file if result is not as was expected
//I have done the work to this part!
//And I can do the functions as follows .. it should be simple.
//*********************
//SendEmail();
//LogIntoFile();
//CallService();
//But I want the above three methods to be present in one place so i dont have to replicate same functionality in different watcher.
//One approach could be to create a seperate class and wrape the above mentioned functions in it, create an instance of that class here and call them.
//Second option, which I am interested in but dont know how to do, is to have this functionality in the parent class which actually creates the tasks and have each watcher use it from HERE ...
}
....
}
//this watcher is responsible for watching over Folder
public class FolderWatcher : IWatcher
{
....
void BeginWatch()
{
///Same as above
}
....
}
First I create a List from an XML file. This can contain multiple instances of DBWatcher which will continously watch a different query result and FolderWatcher which will continously watch different Folders continously.
After the List is created, I call the following function that I call to create a separate Task. I call this function MANY times to create a different set of watchers.
private void _createWatcherThread(IWatcher wat, CancellationTokenSource cancellationToken)
{
//This represents a watcher that will watch some specific area for any activities
IWatcher watcher = wat.Copy();
bool hasWatchBegin = false;
try
{
//run forever
for (;;)
{
//dispose the watcher and stop this thread if CANCEL token has been issued
if (cancellationToken.IsCancellationRequested)
{
((IDisposable)watcher).Dispose();
break;
}
else if (!hasWatchBegin)
{
//This method of a watcher class creates a timer. which will
//continously check the status after a few minutes... So its the
//timer's elapsed method in Watcher object which will send the mail
//& call service etc to update the admin of current status of the watcher.
//this will be called only once in a watcher!
watcher.BeginWatch();
hasWatchBegin = true;
}
}
}
catch (Exception ex)
{
//Watcher has thrown an exception.
//Again, do the following operations
//*********************
//SendEmail();
//LogIntoFile();
//CallService();
}
}
Provided you make your email, logging & webservice calls threadsafe you can pass references to the code which sends to each of these sinks as a closure (Here's Jon Skeet's excellent explanation of c# closures) into the monitoring tasks. Here's an example where you need to launch multiple tasks:
...
void Email(string message){}
void Log(string message){}
void CallWebService(string message){}
void RunMonitoringTask()
{
var task = Task.TaskFactory.StartNew(() =>
{
string message = Monitor();
if( ShouldNotify(message))
{
Email(mesasge);
Log(message);
CallWebService(message);
}
}
)
}
...
EDIT
vs. an infinite monitor loop triggering tasks when necessary:
...
void Email(string message){}
void Log(string message){}
void CallWebService(string message){}
void Monitor()
{
while(true)
{
string message = Monitor();
if(ShouldNotify(message))
{
var task = Task.TaskFactory.StartNew(() =>
{
Email(mesasge);
Log(message);
CallWebService(message);
}
}
}
)
}
...
As far as how to implement these classes, I'd recomend an approach where each of these sinks accepts the message & then offloads it to it's own processing thread/task to avoid blocking your monitoring tasks & holding up the other notifications.
The Progress class is just perfect for this task. It is a means of allowing a long running process to notify someone (usually the caller) of the current progress of that operation.
Progress<string> progress = new Progress<string>();
progress.ProgressChanged += (s, data) => Console.WriteLine(data);
for (int i = 0; i < 2; i++)
Task.Run(() => DoWork(progress));
public static void DoWork(IProgress<string> progress)
{
int i = 0;
while (true)
{
Thread.Sleep(500);//placeholder for real work
progress.Report(i++.ToString());
}
}
If you have different types of information to report at different times then just pass in multiple IProgress instances to the worker method. (Or, if you are reporting the progress of several types of data at the same time wrap all of the data in a composite object.)
Also note that this is capable of handling the synchronization that you have said that you need. Progress instances, when created, capture the value of SynchronizationContext.Current at the time that it's created, and marshal all of the event handlers for the progress changed event into that sync context. So if your application will already have a context (i.e. a UI context from a desktop application) then you get that for free. If you don't have one (i.e. it's a console application) then you'll need to either manually synchronize the event handler with say a lock, or create your own SynchrnonizationContext to set as the current context.

Categories

Resources