Handle exception in consumer queue - c#

public class ProducerConsumerQueue
{
public void EnqueueTask(MyTask task)
{
}
void Work()
{
while (true)
{
try
{
// my task goes here
Thread.Sleep(2000);
}
catch(Exception ex)
{
Log(ex);
}
}
}
}
Producer:
public void Add()
{
MyTask task = new MyTask();
new ProducerConsumerQueue().EnqueueTask(task);
}
I'm in .NET 3.5.
Add() method will be called by my API users. In the example above, inside the method, void work(), I'm catching the exception and logging there.
But instead of that, I would like to catch and rethrow the exception to the user. Sametime, the permanent thread that run inside the while loop, should recover from the exception by continue to the next task in the queue. My short question is - How will I throw exception that happen inside void work(), but still the consumer stay alive for next task in the queue.

Continuing our discussion from the comments, you could possibly do something like collecting all the exceptions occurring when executing a queue of tasks (however you need to execute the queue on cycles) and then throwing it back to the caller.
So something like:
public void ExecuteAllTasks()
{
var exceptions = new List<Exception>();
IEnumerable<MyTask> tasks = GetQueuedTasks(); // get all tasks (or possibly pass them to the method) ...
foreach (MyTask task in tasks)
{
try
{
// execute your tasks here ...
}
catch (Exception ex)
{
// collect all the exceptions
exceptions.Add(ex);
}
}
// throw all the errors at once
if (exceptions.Any())
throw new AggregateException(_exceptions);
}
I hope this helps.

You need to establish some sort of communications between your consumer threads and the main thread. When a consumer encounters an exception, it should notify the main thread and move on to the next task.
Since you're using Winforms, the easiest way to inform the main thread is to use Invoke. See the following question for an example.

Introduce a callback which is invoked when the task has completed:
public interface ICompletionState
{
public ITask Task { get; set; }
public Exception Exception { get; set; }
}
public class CompletionState : ICompletionState
{
public ITask Task { get; set; }
public Exception Exception { get; set; }
public Action<ICompletionState> Callback { get; set; }
}
public class ProducerConsumerQueue
{
ConcurrentQueue<CompletionState> _tasks = new ConcurrentQueue<CompletionState>();
public void EnqueueTask(ITask task, Action<ICompletionState> callback)
{
_tasks.Enqueue(new CompletionState{ Task = task, Callback = callback });
}
void Work()
{
while (true)
{
CompletionState cs;
try
{
if (!_tasks.TryDequeue(out cs))
continue;
cs.Task.Execute();
cs.Callback(cs);
}
catch(Exception ex)
{
cs.Exception = ex;
cs.Callback(cs);
}
}
}
}

Related

C# concurrent: Is it a good idea to use many AutoResetEvent?

Suppose there are many threads calling Do(), and only one worker thread handles the actual job.
void Do(Job job)
{
concurrentQueue.Enqueue(job);
// wait for job done
}
void workerThread()
{
while (true)
{
Job job;
if (concurrentQueue.TryDequeue(out job))
{
// do job
}
}
}
The Do() should wait until the job done before return. So I wrote the following code:
class Task
{
public Job job;
public AutoResetEvent ev;
}
void Do(Job job)
{
using (var ev = new AutoResetEvent(false))
{
concurrentQueue.Enqueue(new Task { job = job, ev = ev }));
ev.WaitOne();
}
}
void workerThread()
{
while (true)
{
Task task;
if (concurrentQueue.TryDequeue(out task))
{
// do job
task.ev.Set();
}
}
}
After some tests I found it works as expected. However I'm not sure is it a good way to allocate many AutoResetEvents, or is there a better way to accomplish?
Since all clients must wait a single thread to do the job, there is no real need for using a queue. So I suggest to use the Monitor class instead, and specifically the Wait/Pulse functionality. It is a bit low level and verbose though.
class Worker<TResult> : IDisposable
{
private readonly object _outerLock = new object();
private readonly object _innerLock = new object();
private Func<TResult> _currentJob;
private TResult _currentResult;
private Exception _currentException;
private bool _disposed;
public Worker()
{
var thread = new Thread(MainLoop);
thread.IsBackground = true;
thread.Start();
}
private void MainLoop()
{
lock (_innerLock)
{
while (true)
{
Monitor.Wait(_innerLock); // Wait for client requests
if (_disposed) break;
try
{
_currentResult = _currentJob.Invoke();
_currentException = null;
}
catch (Exception ex)
{
_currentException = ex;
_currentResult = default;
}
Monitor.Pulse(_innerLock); // Notify the waiting client that the job is done
}
} // We are done
}
public TResult DoWork(Func<TResult> job)
{
TResult result;
Exception exception;
lock (_outerLock) // Accept only one client at a time
{
lock (_innerLock) // Acquire inner lock
{
if (_disposed) throw new InvalidOperationException();
_currentJob = job;
Monitor.Pulse(_innerLock); // Notify worker thread about the new job
Monitor.Wait(_innerLock); // Wait for worker thread to process the job
result = _currentResult;
exception = _currentException;
// Clean up
_currentJob = null;
_currentResult = default;
_currentException = null;
}
}
// Throw the exception, if occurred, preserving the stack trace
if (exception != null) ExceptionDispatchInfo.Capture(exception).Throw();
return result;
}
public void Dispose()
{
lock (_outerLock)
{
lock (_innerLock)
{
_disposed = true;
Monitor.Pulse(_innerLock); // Notify worker thread to exit loop
}
}
}
}
Usage example:
var worker = new Worker<int>();
int result = worker.DoWork(() => 1); // Accepts a function as argument
Console.WriteLine($"Result: {result}");
worker.Dispose();
Output:
Result: 1
Update: The previous solution is not await-friendly, so here is one that allows proper awaiting. It uses a TaskCompletionSource for each job, stored in a BlockingCollection.
class Worker<TResult> : IDisposable
{
private BlockingCollection<TaskCompletionSource<TResult>> _blockingCollection
= new BlockingCollection<TaskCompletionSource<TResult>>();
public Worker()
{
var thread = new Thread(MainLoop);
thread.IsBackground = true;
thread.Start();
}
private void MainLoop()
{
foreach (var tcs in _blockingCollection.GetConsumingEnumerable())
{
var job = (Func<TResult>)tcs.Task.AsyncState;
try
{
var result = job.Invoke();
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
}
public Task<TResult> DoWorkAsync(Func<TResult> job)
{
var tcs = new TaskCompletionSource<TResult>(job,
TaskCreationOptions.RunContinuationsAsynchronously);
_blockingCollection.Add(tcs);
return tcs.Task;
}
public TResult DoWork(Func<TResult> job) // Synchronous call
{
var task = DoWorkAsync(job);
try { task.Wait(); } catch { } // Swallow the AggregateException
// Throw the original exception, if occurred, preserving the stack trace
if (task.IsFaulted) ExceptionDispatchInfo.Capture(task.Exception.InnerException).Throw();
return task.Result;
}
public void Dispose()
{
_blockingCollection.CompleteAdding();
}
}
Usage example
var worker = new Worker<int>();
int result = await worker.DoWorkAsync(() => 1); // Accepts a function as argument
Console.WriteLine($"Result: {result}");
worker.Dispose();
Output:
Result: 1
From a synchronization perspective this is working fine.
But it seems useless to do it this way. If you want to execute jobs one after the other you can just use a lock:
lock (lockObject) {
RunJob();
}
What is your intention with this code?
There also is an efficiency question because each task creates an OS event and waits on it. If you use the more modern TaskCompletionSource this will use the same thing under the hood if you synchronously wait on that task. You can use asynchronous waiting (await myTCS.Task;) to possibly increase efficiency a bit. Of course this infects the entire call stack with async/await. If this is a fairly low volume operation you won't gain much.
In general I think would work, although when you say "many" threads are calling Do() this might not scale well ... suspended threads use resources.
Another problem with this code is that at idle times, you will have a "hard loop" in "workerThread" which will cause your application to return high CPU utilization times. You may want to add this code to "workerThread":
if (concurrentQueue.IsEmpty) Thread.Sleep(1);
You might also want to introduce a timeout to the WaitOne call to avoid a log jam.

Producer/ Consumer pattern using threads and EventWaitHandle

I guess it is sort of a code review, but here is my implementation of the producer / consumer pattern. What I would like to know is would there be a case in which the while loops in the ReceivingThread() or SendingThread() methods might stop executing. Please note that EnqueueSend(DataSendEnqeueInfo info) is called from multiple different threads and I probably can't use tasks here since I definitely have to consume commands in a separate thread.
private Thread mReceivingThread;
private Thread mSendingThread;
private Queue<DataRecievedEnqeueInfo> mReceivingThreadQueue;
private Queue<DataSendEnqeueInfo> mSendingThreadQueue;
private readonly object mReceivingQueueLock = new object();
private readonly object mSendingQueueLock = new object();
private bool mIsRunning;
EventWaitHandle mRcWaitHandle;
EventWaitHandle mSeWaitHandle;
private void ReceivingThread()
{
while (mIsRunning)
{
mRcWaitHandle.WaitOne();
DataRecievedEnqeueInfo item = null;
while (mReceivingThreadQueue.Count > 0)
{
lock (mReceivingQueueLock)
{
item = mReceivingThreadQueue.Dequeue();
}
ProcessReceivingItem(item);
}
mRcWaitHandle.Reset();
}
}
private void SendingThread()
{
while (mIsRunning)
{
mSeWaitHandle.WaitOne();
while (mSendingThreadQueue.Count > 0)
{
DataSendEnqeueInfo item = null;
lock (mSendingQueueLock)
{
item = mSendingThreadQueue.Dequeue();
}
ProcessSendingItem(item);
}
mSeWaitHandle.Reset();
}
}
internal void EnqueueRecevingData(DataRecievedEnqeueInfo info)
{
lock (mReceivingQueueLock)
{
mReceivingThreadQueue.Enqueue(info);
mRcWaitHandle.Set();
}
}
public void EnqueueSend(DataSendEnqeueInfo info)
{
lock (mSendingQueueLock)
{
mSendingThreadQueue.Enqueue(info);
mSeWaitHandle.Set();
}
}
P.S the idea here is that am using WaitHandles to put thread to sleep when the queue is empty, and signal them to start when new items are enqueued.
UPDATE
I am just going to leave this https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/ ,for people who might be trying to implement Producer/Consumer pattern using TPL or tasks.
Use a BlockingCollection instead of Queue, EventWaitHandle and lock objects:
public class DataInfo { }
private Thread mReceivingThread;
private Thread mSendingThread;
private BlockingCollection<DataInfo> queue;
private CancellationTokenSource receivingCts = new CancellationTokenSource();
private void ReceivingThread()
{
try
{
while (!receivingCts.IsCancellationRequested)
{
// This will block until an item is added to the queue or the cancellation token is cancelled
DataInfo item = queue.Take(receivingCts.Token);
ProcessReceivingItem(item);
}
}
catch (OperationCanceledException)
{
}
}
internal void EnqueueRecevingData(DataInfo info)
{
// When a new item is produced, just add it to the queue
queue.Add(info);
}
// To cancel the receiving thread, cancel the token
private void CancelReceivingThread()
{
receivingCts.Cancel();
}
Personally, for simple producer-consumer problems, I would just use BlockingCollection. There would be no need to manually code your own synchronization logic. The consuming threads will also block if there are no items present in the queue.
Here is what your code might look like if you use this class:
private BlockingCollection<DataRecievedEnqeueInfo> mReceivingThreadQueue = new BlockingCollection<DataRecievedEnqeueInfo>();
private BlockingCollection<DataSendEnqeueInfo> mSendingThreadQueue = new BlockingCollection<DataSendEnqeueInfo>();
public void Stop()
{
// No need for mIsRunning. Makes the enumerables in the GetConsumingEnumerable() calls
// below to complete.
mReceivingThreadQueue.CompleteAdding();
mSendingThreadQueue.CompleteAdding();
}
private void ReceivingThread()
{
foreach (DataRecievedEnqeueInfo item in mReceivingThreadQueue.GetConsumingEnumerable())
{
ProcessReceivingItem(item);
}
}
private void SendingThread()
{
foreach (DataSendEnqeueInfo item in mSendingThreadQueue.GetConsumingEnumerable())
{
ProcessSendingItem(item);
}
}
internal void EnqueueRecevingData(DataRecievedEnqeueInfo info)
{
// You can also use TryAdd() if there is a possibility that you
// can add items after you have stopped. Otherwise, this can throw an
// an exception after CompleteAdding() has been called.
mReceivingThreadQueue.Add(info);
}
public void EnqueueSend(DataSendEnqeueInfo info)
{
mSendingThreadQueue.Add(info);
}
As suggested in comments, you also can give a try to the TPL Dataflow blocks.
As far as I can see, you have two similar pipelines, for receive and send, so I assume that your class hierarchy is like this:
class EnqueueInfo { }
class DataRecievedEnqeueInfo : EnqueueInfo { }
class DataSendEnqeueInfo : EnqueueInfo { }
We can assemble an abstract class which will encapsulate the logic for creating the pipeline, and providing the interface for processing the items, like this:
abstract class EnqueueInfoProcessor<T>
where T : EnqueueInfo
{
// here we will store all the messages received before the handling
private readonly BufferBlock<T> _buffer;
// simple action block for actual handling the items
private ActionBlock<T> _action;
// cancellation token to cancel the pipeline
public EnqueueInfoProcessor(CancellationToken token)
{
_buffer = new BufferBlock<T>(new DataflowBlockOptions { CancellationToken = token });
_action = new ActionBlock<T>(item => ProcessItem(item), new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount,
CancellationToken = token
});
// we are linking two blocks so all the items from buffer
// will flow down to action block in order they've been received
_buffer.LinkTo(_action, new DataflowLinkOptions { PropagateCompletion = true });
}
public void PostItem(T item)
{
// synchronously wait for posting to complete
_buffer.Post(item);
}
public async Task SendItemAsync(T item)
{
// asynchronously wait for message to be posted
await _buffer.SendAsync(item);
}
// abstract method to implement
protected abstract void ProcessItem(T item);
}
Note that you also can encapsulate the link between two blocks by using the Encapsulate<TInput, TOutput> method, but in that case you have to properly handle the Completion of the buffer block, if you're using it.
After this, we just need to implement two methods for receive and send handle logic:
public class SendEnqueueInfoProcessor : EnqueueInfoProcessor<DataSendEnqeueInfo>
{
SendEnqueueInfoProcessor(CancellationToken token)
: base(token)
{
}
protected override void ProcessItem(DataSendEnqeueInfo item)
{
// send logic here
}
}
public class RecievedEnqueueInfoProcessor : EnqueueInfoProcessor<DataRecievedEnqeueInfo>
{
RecievedEnqueueInfoProcessor(CancellationToken token)
: base(token)
{
}
protected override void ProcessItem(DataRecievedEnqeueInfo item)
{
// recieve logic here
}
}
You also can create more complicated pipeline with TransformBlock<DataRecievedEnqeueInfo, DataSendEnqeueInfo>, if your message flow is about a ReceiveInfo message became SendInfo.

Simple in-memory message queue

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.

How to properly notify caller on failure of the asynchronous operation?

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.

Stopping a Specific Thread and Starting It Up Again From Windows Service

I want to know how to stop and restart a thread.
I create N amount of threads, depending on conditions returned from a database. These are long running processes which should never stop but should I get a critical error within the thread I want to completely kill the thread and start it up like new.
The code which I use currently to start the threads:
foreach (MobileAccounts MobileAccount in ReceiverAccounts)
{
Receiver rec = new Receiver();
ThreadStart starterParameters = delegate { rec.StartListener(MobileAccount); };
Thread FeedbackThread = new Thread(starterParameters);
FeedbackThread.Name = MobileAccount.FriendlyName;
FeedbackThread.Start();
Thread.Sleep(1000);
}
You can write your own listener and manage its thread within it.
something like:
public class AccountListener
{
private Thread _worker = null;
private MobileAccount _mobileAccount;
public AccountListener(MobileAccount mobileAccount)
{
_mobileAccount = mobileAccount;
}
protected void Listen()
{
try
{
DoWork();
}
catch (Exception exc)
{
}
}
protected virtual void DoWork()
{
Console.WriteLine(_mobileAccount);
}
public void Start()
{
if (_worker == null)
{
_worker = new Thread(Listen);
}
_worker.Start();
}
public void Stop()
{
try
{
_worker.Abort();
}
catch (Exception)
{
//thrad abort exception
}
finally
{
_worker = null;
}
}
}

Categories

Resources