Class FeatureManager manages some feature and looks something like this:
public class FeatureManager
{
public event EventHandler FeatureEnabledChangedEvent;
private void OnFeatureEnabledChanged()
{
if (FeatureEnabledChangedEvent != null)
{
FeatureEnabledChangedEvent(this, EventArgs.Empty);
}
}
public event EventHandler FeatureEnableBusyChangedEvent;
private void OnFeatureEnableBusyChanged()
{
if (FeatureEnableBusyChangedEvent != null)
{
FeatureEnableBusyChangedEvent(this, EventArgs.Empty);
}
}
public event EventHandler FeatureEnableFailedEvent;
private void OnFeatureEnableFailed(FeatureEnableFailedEventArgs args)
{
if (FeatureEnableFailedEvent!= null)
{
FeatureEnableFailedEvent(this, args);
}
}
private bool _isFeatureEnabled
public bool IsFeatureEnabled
{
get
{
return _isFeatureEnabled;
}
private set
{
if (_isFeatureEnabled != value)
{
_isFeatureEnabled = value;
OnFeatureEnabledChanged();
}
}
}
private bool _isFeatureEnableBusy;
public bool IsFeatureEnableBusy
{
get
{
return _isFeatureEnableBusy;
}
private set
{
if (_isFeatureEnableBusy != value)
{
_isFeatureEnableBusy = value;
OnFeatureEnableBusyChanged();
}
}
}
public async Task EnableFeature()
{
IsFeatureEnableBusy = true;
try
{
// By its nature, process of enabling this feature is asynchronous.
await EnableFeatureImpl(); // can throw exception
IsFeatureEnabled = true;
}
catch(Exception exc)
{
OnFeatureEnableFailed(new FeatureEnableFailedEventArgs(exc.Message));
}
finally
{
IsFeatureEnableBusy = false;
}
}
}
UI class FeatureView has to be notified when:
IsFeatureEnableBusy changes (or, in other words when EnableFeature is being executed - in order to disable some controls)
IsFeatureEnabled changes
EnableFeature fails (when it throws exception in which case FeatureView displays error message
to the user)
EnableFeature can be called from some Engine class E (automatically, during the initialization on the application's launch) and also from FeatureView (when user presses 'Enable' button).
In order to satisfy requirement where FeatureView has to be notified when EnableFeature fails after it's been called by E, I added an event FeatureEnableFailedEvent.
When E calls EnableFeature and EnableFeature throws an exception, FeatureView receives FeatureEnableFailedEvent and displays error message. But when FeatureView itself calls EnableFeature and EnableFeature fails, FeatureView catches thrown exception but also gets notified on this failure from FeatureEnableFailedEvent so error handler is called twice. How to avoid this?
One solution is to declare EnableFeature as an old-style async method (and use BackgroundWorker) as in the following snippet:
public class FeatureManager
{
public void EnableFeatureAsync()
{
var bgw = new BackgroundWorker();
bgw.DoWork += (sender, e) =>
{
IsFeatureEnableBusy = true;
EnableFeatureImpl(); // can throw exception
};
bgw.RunWorkerCompleted += (sender, args) =>
{
IsFeatureEnableBusy = false;
if (args.Error == null)
{
IsFeatureEnabled = true;
}
else
{
OnFeatureEnableFailed(new FeatureEnableFailedEventArgs(args.Error.Message));
}
};
bgw.RunWorkerAsync();
}
}
In this case, caller of EnableFeatureAsync can assume that this method runs asynchronously (suffix Async in method's name should be a hint) and that it has to subscribe to FeatureEnableFailedEvent if want to be notified on the method failure. This way FeatureView gets notified on EnableFeatureAsync failure only once so error handler is called once as it should be.
Is this a good approach? Could this be achieved by still using async/await in some way? Is it good assuming that suffix Async in method's name is a good enough hint to callers so they know that this method runs as asynchronous one and that they have to look for some events to subscribe to?
As commented by #svick, I also don't see why your FeatureView catches the exception and also gets the event, when the exception is not rethrown in the handler of FeatureManager. But here is an different approach, which I'd prefer over yours based on events:
Use TaskCompletionSource to let the view know when the enablement of a feature did throw an exception even when FeatureView is not the caller of EnableFeature() (btw, by convention the method should also be named EnableFeatureAsync in the first example).
public class FeatureManager
{
public TaskCompletionSource<bool> FeatureCompleted { get; private set; }
// if you still need this property
public bool IsFeatureEnabled
{
get { return FeatureCompleted.Task.IsCompleted; }
}
public FeatureManager() {}
public async Task EnableFeature()
{
IsFeatureEnableBusy = true;
try
{
// By its nature, process of enabling this feature is asynchronous.
await EnableFeatureImpl(); // can throw exception
this.FeatureCompleted.TrySetResult(true);
}
catch(Exception exc)
{
this.FeatureCompleted.TrySetException(exc);
}
finally
{
IsFeatureEnableBusy = false;
}
}
}
Your FeatureView instance now needs to await the Task of the TaskCompletionSource. The code could look like this:
public class FeatureView
{
// if you still need this property
public async void HandleFeatureCompleted(FeatureManager fm)
{
try
{
await fm.FeatureCompleted.Task;
}
catch(Exception e)
{
// handle exception
}
}
}
You have to provide the correct FeatureManager instance to your view. I'm not sure if this approach is appropriate if you have hundredths or even thousands of FeatureManager instances messages. I'd be happy if more somebody of the commenters could provide feedback about this.
Related
I have the follwing code:
public class Messenger
{
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
public void OnPrivateMessage(string message)
{
if (MessageReceived != null)
{
MessageReceived(this, new MessageReceivedEventArgs(message));
}
}
public void OnPublicMessage(string message)
{
if (MessageReceived != null)
{
MessageReceived(this, new MessageReceivedEventArgs(message));
}
}
}
OnPrivateMessage and OnPublicMessage are called from different threads. Is it thread safe to call the same event from different threads?
Quick and dirty way would be to have a lock. Probably the best place to put that lock would be inside the MessageReceived method, so that it would kick in whenever that method is called. SOmething like this:
private object _messageReceivedLock = new object();
private void MessageReceived(Sender object, MessageReceivedEventArgs msg)
{
lock (_messageReceivedLock)
{
// the rest of the MessageReceived body goes here
}
}
Our existing implementation of domain events limits (by blocking) publishing to one thread at a time to avoid reentrant calls to handlers:
public interface IDomainEvent {} // Marker interface
public class Dispatcher : IDisposable
{
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
// Subscribe code...
public void Publish(IDomainEvent domainEvent)
{
semaphore.Wait();
try
{
// Get event subscriber(s) from concurrent dictionary...
foreach (Action<IDomainEvent> subscriber in eventSubscribers)
{
subscriber(domainEvent);
}
}
finally
{
semaphore.Release();
}
}
// Dispose pattern...
}
If a handler publishes an event, this will deadlock.
How can I rewrite this to serialize calls to Publish? In other words, if subscribing handler A publishes event B, I'll get:
Handler A called
Handler B called
while preserving the condition of no reentrant calls to handlers in a multithreaded environment.
I do not want to change the public method signature; there's no place in the application to call a method to publish a queue, for instance.
We came up with a way to do it synchronously.
public class Dispatcher : IDisposable
{
private readonly ConcurrentQueue<IDomainEvent> queue = new ConcurrentQueue<IDomainEvent>();
private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
// Subscribe code...
public void Publish(IDomainEvent domainEvent)
{
queue.Enqueue(domainEvent);
if (IsPublishing)
{
return;
}
PublishQueue();
}
private void PublishQueue()
{
IDomainEvent domainEvent;
while (queue.TryDequeue(out domainEvent))
{
InternalPublish(domainEvent);
}
}
private void InternalPublish(IDomainEvent domainEvent)
{
semaphore.Wait();
try
{
// Get event subscriber(s) from concurrent dictionary...
foreach (Action<IDomainEvent> subscriber in eventSubscribers)
{
subscriber(domainEvent);
}
}
finally
{
semaphore.Release();
}
// Necessary, as calls to Publish during publishing could have queued events and returned.
PublishQueue();
}
private bool IsPublishing
{
get { return semaphore.CurrentCount < 1; }
}
// Dispose pattern for semaphore...
}
}
You will have to make Publish asynchronous to achieve that. Naive implementation would be as simple as:
public class Dispatcher : IDisposable {
private readonly BlockingCollection<IDomainEvent> _queue = new BlockingCollection<IDomainEvent>(new ConcurrentQueue<IDomainEvent>());
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
public Dispatcher() {
new Thread(Consume) {
IsBackground = true
}.Start();
}
private List<Action<IDomainEvent>> _subscribers = new List<Action<IDomainEvent>>();
public void AddSubscriber(Action<IDomainEvent> sub) {
_subscribers.Add(sub);
}
private void Consume() {
try {
foreach (var #event in _queue.GetConsumingEnumerable(_cts.Token)) {
try {
foreach (Action<IDomainEvent> subscriber in _subscribers) {
subscriber(#event);
}
}
catch (Exception ex) {
// log, handle
}
}
}
catch (OperationCanceledException) {
// expected
}
}
public void Publish(IDomainEvent domainEvent) {
_queue.Add(domainEvent);
}
public void Dispose() {
_cts.Cancel();
}
}
It can't be done with that interface. You can process the event subscriptions asynchronously to remove the deadlock while still running them serially, but then you can't guarantee the order you described. Another call to Publish might enqueue something (event C) while the handler for event A is running but before it publishes event B. Then event B ends up behind event C in the queue.
As long as Handler A is on equal footing with other clients when it comes to getting an item in the queue, it either has to wait like everyone else (deadlock) or it has to play fairly (first come, first served). The interface you have there doesn't allow the two to be treated differently.
That's not to say you couldn't get up to some shenanigans in your logic to attempt to differentiate them (e.g. based on thread id or something else identifiable), but anything along those lines would unreliable if you don't control the subscriber code as well.
I have extended the System.Collections.Concurrent.ConcurrentQueue to raise events as objects are enqueued. I now need to be able to use System.Management.Automation.WriteObject/WriteVerbose/WriteDebug methods to write the objects from the aforementioned events. However, I receive the following error when attempting to use System.Management.Automation.WriteObject/WriteVerbose/WriteDebug in the event handler.
Does anyone know how I can marshal the events back to the main thread so that I can use the System.Management.Automation.WriteObject/WriteVerbose/WriteDebug methods?
Here is my extended ConcurrentQueue class.
public class ConcurrentQueueEx<T> : ConcurrentQueue<T>
{
#region Private Variables
private Guid _id;
#endregion
#region Public Accessors
public Guid Id
{
get { return this._id; }
}
#endregion
#region Event Declarations
public event PSObjectAddedEventHandler PSObjectAdded;
protected virtual void OnPSObjectAdded(Guid parentId, object obj)
{
PSObjectAddedEventHandler handler = PSObjectAdded;
if (handler != null)
{
PSObjectAddedEventArgs oae = new PSObjectAddedEventArgs();
oae._ParentId = parentId;
oae._Object = obj;
handler(oae);
}
}
#endregion
#region Public Functions
public new virtual void Enqueue(T item)
{
base.Enqueue(item);
OnPSObjectAdded(this._id, item);
}
public virtual void Push(T item)
{
base.Enqueue(item);
OnPSObjectAdded(this._id, item);
}
public virtual T Pop()
{
T obj;
base.TryDequeue(out obj);
return obj;
}
#endregion
}
Here are the relevant sections from the cmdlet.
protected override void BeginProcessing()
{
base.BeginProcessing();
_messageQueue = new ConcurrentQueueEx<object>();
_messageQueue.PSObjectAdded += _messageQueue_PSObjectAdded;
_resultQueue = new ConcurrentQueueEx<object>();
_resultQueue.PSObjectAdded += _resultQueue_PSObjectAdded;
}
private void _resultQueue_PSObjectAdded(PSObjectAddedEventArgs e)
{
WriteObject(e._Object);
}
private void _messageQueue_PSObjectAdded(PSObjectAddedEventArgs e)
{
WriteVerbose(e._Object.ToString());
}
Here are the exception details.
System.Management.Automation.PSInvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services.
Source=System.Management.Automation
What is the "main thread" doing while the thread on which the events are raised is queued?
If the main thread is blocked then you could make it wait on a synchronization object and then have it dequeue the objects.
If the thread is off doing something else, then you need to need it to either get it interrupted (by, say an event) or else have it poll the queue. I assume you'll want to do the first. In which case you'll need to register for an event in your cmdlet and fire the event from the other thread. The second answer here shows how to do this.
I was given a generic API class, that contains a custom event which always needs to be invoked by the main UI thread.
My job is to banish these invocation call from the custom class, to make it "painless".
It should be synchronized like the default events in WinForms (eg the Timer "Elapsed" event, which also needs no invocation when it published values to a text box)
Is it possible to solve this, since the custom class needs to know where to invoke?
Here's the (important part of the) code:
public class ContactSensorHelper
{
public event OnReleaseStateChanged ReleaseStateChanged;
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (ReleaseStateChanged != null)
ReleaseStateChanged(new ContactSensorEventArgs()
{
State = recentReleaseState
});
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
The call from main UI:
public void SensorInit()
{
//....
sensorHelper.ReleaseStateChanged += releaseStateChanged;
//....
}
private void releaseStateChanged(ContactSensorEventArgs e)
{
//example
textBox1.Text = e.State.ToString(); // Thread exception (obviously)
}
Does anybody have me a hint to start?
You could do this by using your own event calling, and storing a reference to the thread, when the event is attached.
With the event add/remove syntax, you can have the caller attach to the event like before, but internally you store a list, with a reference to the thread (using an AsyncOperation) and the delegate to be called (used a Tuple containing both in the example)
Below is an example. I tested it, and it worked as expected when testing, but you might have to add some locking of the list to make it thread safe in case events are added/removed simultaneously.
public class ContactSensorHelper:IDisposable
{
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (statechangedList.Count > 0)
{
var e = new ContactSensorEventArgs()
{
State = recentReleaseState
};
statechangedList.ForEach(t =>
t.Item1.Post(o => t.Item2((ContactSensorEventArgs)o), e));
}
}
List<Tuple<AsyncOperation, OnReleaseStateChanged>> statechangedList = new List<Tuple<AsyncOperation,OnReleaseStateChanged>>();
public event OnReleaseStateChanged ReleaseStateChanged
{
add
{
var op = AsyncOperationManager.CreateOperation(null);
statechangedList.Add(Tuple.Create(op, value));
}
remove
{
var toremove = statechangedList.Where(t => t.Item2 == value).ToArray();
foreach (var t in toremove)
{
t.Item1.OperationCompleted();
statechangedList.Remove(t);
}
}
}
public void Dispose()
{
statechangedList.ForEach(t => t.Item1.OperationCompleted());
statechangedList.Clear();
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
I have a monitor class which monitors a device and reports if that device successfully receives usable data. This can happen anytime.
A client creates its own monitor by passing delegates, starts it and waits for either the successfully read data or a kind of domain specific exception type (one base exception type)
What would be the idiomatic way of throwing subtypes of the base exception type and enable the client to respond to each subtype individually?
public class MyMonitor
{
private SuccessHandler _successHandler;
private ErrorHandler _errorHandler;
public delegate void SuccessHandler(MyDTO result);
public delegate void ErrorHandler(MyBaseException exception);
public MyMonitor(SuccessHandler successHandler, ErrorHandler errorHandler) {
_successHandler = successHandler;
_errorHandler = errorHandler;
}
public void start() {
try {
_successHandler(new MyDTP().doSomethingRisky());
} catch(Exception e) {
_errorHandler(e);
}
}
}
public class Client {
static void Main(string[] args) {
MyMonitor monitor = new MyMonitor(new MyMonitor.SuccessHandler(handleSuccess), new MyMonitor.ErrorHandler(handleException));
monitor.start();
}
static void handleSuccess(MyDTO result) {
// do something with result
}
static void handleException(MyBaseException e) {
try {
throw e;
} catch(UserException mbe) {
// present message to user
} catch(DataNotFoundException se) {
// log error and show generic error message
} catch(UnexpectedException ue) {
// log error and try to hide it from the user
}
}
}
So, why don't you handle the exceptions in your main instead of the monitor-class?
If that isn't an option, you have (at least) two alternatives:
static void handleException(MyBaseException e)
{
if (e is UserException)
{
// present message to user
}
else if (e is DataNotFoundException)
{
// log error and show generic error message
}
elseif (e is UnexpectedException)
{
// log error and try to hide it from the user
}
else
{
// might want to rethrow the exception, do a general handling,...
}
}
That way you don't have to rethrow the exception, just to catch it again.
But this can get ugly if you have many subtypes to handle and here is where multidispatch comes in.
static void HandleException(MyBaseException e)
{
HandleSubException((dynamic)e);
}
static void HandleSubException(MyBaseException e)
{
// might want to rethrow the exception, do a general handling,...
}
static void HandleSubException(DataNotFoundExceptione)
{
// log error and show generic error message
}
static void HandleSubException(UnexpectedException e)
{
// log error and try to hide it from the user
}
static void HandleSubException(UserExceptione)
{
// present message to user
}
Now you can tend to each exception in its own method and is much easier to read and maintain.
Having said that, I'm not entirely sure if this falls under best practice.