Await/async doesn't work as expected - c#

I'm getting started on async/await using. I've written simple application using WPF based on MVVM pattern, but it doesn't work as I expected. The program works as there were no asynchronous functions: after executing execute function it freezes and unfreezes only after loop function ended.
Please tell me what part did I get wrong. I'd appreciate any feedback. :)
Here is my modelview class. It inherits from wpf class, that contains definitions of standard wpf functions like OnPropertyChanged.
public class ModelView : wpf
{
string _state;
public string state { get { return _state; } set { _state = value; OnPropertyChanged("state"); } }
public DelegateCommand work { get; set; }
public ModelView()
{
state = "Program started";
work=new DelegateCommand(_work);
}
async void _work(object parameter)
{
state = "Working...";
int j=await loop();
state = "Done: " + j;
}
async Task<int> loop()
{
int i;
for(i=0;i<1000000000;i++);
return i;
}
}

There isn't an asynchronous part in your code. Simply using the async keyword doesn't make it so. Use Task.Run instead if you wish to offload synchronous code to a different thread:
async void _work(object parameter)
{
status = "Working...";
int j=await Task.Run(() => loop());
status = "Done: " + j;
}
int loop()
{
int i;
for(i=0;i<1000000000;i++);
return i;
}
If you actually have an asynchronous operation, you can use that instead:
async void _work(object parameter)
{
status = "Working...";
await Task.Delay(1000);
status = "Done: " + j;
}
Guideline: If your "async" method doesn't have an await inside it, it isn't asynchronous.

You aren't doing anything async. The async keyword does not mean "creates another thread"; it is quite complex, but basically it allows code to work with continuations when another asynchronous operation completes. Good examples would include asynchronous database access, file access, network access, etc. It can also include asynchronous long-running operations via threads and Task.Run. But: you aren't doing that. There is nothing incomplete to await - the code just runs on the main thread. It only schedules a continuation when there is something incomplete to continue from.
To quote the compiler (talking about the loop method):
Warning This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

It looks like the problem is your loop function. Even though you have declared it as async there are no await statements in it and you should get a compiler warning as #Marc Gravell pointed out.
The function spins through large number of loop iterations hogging the UI thread which will cause the blocking, it then returns the final value.
So your loop function is in fact a synchronous function. Assuming you were doing this for test purposes a good way to simulate an async operation is to use Task.Delay.
For example:
async Task<int> loop()
{
await Task.Delay(5000);
return 1;
}

Related

I have a few questions about asynchronous programming

I use the following code and it works, but I'm not sure if it is the best/recommanded way to do asynchronous programming.
For example, is the following code necessary or can I call UpdateUsername() without writing this code?
NewUsername();
public async void NewUsername()
{
await UpdateUsername();
}
I use "await" twice in my code, is it necessary to use it twice?
Is it always necessary to use "await" before some code that is marked with async, like the "await" before "PlayFabClientAPI.UpdateUserTitleDisplayNameAsync"?
private async Task UpdateUsername()
{
var resultupdatee = await PlayFabClientAPI.UpdateUserTitleDisplayNameAsync(new UpdateUserTitleDisplayNameRequest()
{
DisplayName = "NewName"
});
if (resultupdatee.Error != null)
{
// Handle error if any
Console.WriteLine(resultupdatee.Error.GenerateErrorReport());
}
else
{
Console.WriteLine("User display name updated");
}
}
When gets the line "int Count = 10;" executed? Is it possible that this line gets excuted before "await PlayFabClientAPI.UpdateUserTitleDisplayNameAsync" finished?
I want that "int Count = 10;" is only then executed after the code UpdateUsername() is finished.
NewUsername();
int Count = 10;
Based on that NewUsername returns void, it does not support awaiting, so there is no need to make it an asynchronous method.
public void NewUsername()
{
UpdateUsername();
}
But you need to ensure that int Count = 10 executes after the asynchronous task is done, we cannot let NewUsername return void, otherwise there is nothing to await.
Based on your code I assume that there is no further operation after UpdateUsername, so there is no need for NewUsername to be a async method
public Task NewUsername()
{
return UpdateUsername();
}
But if there is, you need
public async Task NewUsername()
{
await UpdateUsername();
//Operations after UpdateUsername is done.
}
As long as NewUsername returns Task, you can use await to ensure int Count = 10; executes after everything is done.
//Must be in an async function
await NewUsername();
int Count = 10;
As you can see, if there is some operation in a function A that needs to execute after another asynchronous function B is done, then function A needs to be asynchronous. In other words, asynchronous function is transitive. An asynchronous callee makes the caller asynchronous, unless you don't need to ensure the sequence of execution or you block the thread to wait synchronously.
Conclusion, there are 3 ways to do after calling an asynchronous function:
Waiting asynchronously, which is to await it to add operations after the callee is done. This makes the caller asynchronous.
Waiting synchronously, which is to call Wait method on the returned Task value to block the current thread for the completion. This is not recommanded for it makes the asynchronous callings meaningless.
Waiving the returned Task value of the asynchronous callee or the callee returning void, which is not to go ahead executing without waiting for the completion of the callee.

Is this code synchronous or asynchronous?

Code below that I try to learn Task class. From the output, I see the main thread and task thread are running at the same time. But i get warning message in async method saying that:
"Warning 1 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread."
so then is the code below synchronous?
namespace SampleThreadTaskClass
{
class Program
{
static void Main(string[] args)
{
Task task = new Task(ProcessDataAsync);
task.Start();
Console.WriteLine("Enter any key");
string input = Console.ReadLine();
Console.WriteLine("You entered: " + input);
Console.ReadLine();
}
static async void ProcessDataAsync()
{
for (int i = 0; i < 20; i++)
{
Thread.Sleep(500);
Console.WriteLine("processing... " + i);
}
}
}
}
The ProcessDataAsync method is indeed synchronous. It claims to be asynchronous and it is lying in that claim.
You then provide that synchronous method to the constructor for Task which will execute that synchronous method in a thread pool thread (which you shouldn't use by the way; if you want to execute a synchronous method in a thread pool thread you should use Task.Run).
Of course, for your case you don't want to execute this method in a thread pool thread. You're asynchronous operation is just waiting. There's no need to schedule a new thread pool thread to just sit there doing nothing for seconds at a time.
You should make ProcessDataAsync actually be asynchronous, using Task.Delay to create a Task that will complete in a given interval of time, which you can await, and then you can simply call ProcessDataAsync when you want to start your asynchronous method and it will actually be asynchronous.
What you should try is write some code before starting the task, then start the task in an asynchronous method. Then have another async method like ProcessDataAsync use await operator in the first async method. This way you can come to know about the async behavior.
This would help
static async Task ProcessDataAsync()
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(500);
Console.WriteLine("processing... " + i);
}
}
When you change your return type to async, you need to change your call toProcessDataAsyc to this
var task = Task.Run(ProcessDataAsync);
And yes your code is async.
Yes it is. Thread.Sleep() is not asynchronous.
See Explanation: https://msdn.microsoft.com/en-us/library/hh156528.aspx#Example
Try delay instead, then you can add an await in front of it,
await Task.Delay(10000);

Calling async methods from a synchronous context

I'm calling a service over HTTP (ultimately using the HttpClient.SendAsync method) from within my code. This code is then called into from a WebAPI controller action. Mostly, it works fine (tests pass) but then when I deploy on say IIS, I experience a deadlock because caller of the async method call has been blocked and the continuation cannot proceed on that thread until it finishes (which it won't).
While I could make most of my methods async I don't feel as if I have a basic understanding of when I'd must do this.
For example, let's say I did make most of my methods async (since they ultimately call other async service methods) how would I then invoke the first async method of my program if I built say a message loop where I want some control of the degree of parallelism?
Since the HttpClient doesn't have any synchronous methods, what can I safely presume to do if I have an abstraction that isn't async aware? I've read about the ConfigureAwait(false) but I don't really understand what it does. It's strange to me that it's set after the async invocation. To me that feels as if a race waiting to happen... however unlikely...
WebAPI example:
public HttpResponseMessage Get()
{
var userContext = contextService.GetUserContext(); // <-- synchronous
return ...
}
// Some IUserContextService implementation
public IUserContext GetUserContext()
{
var httpClient = new HttpClient();
var result = httpClient.GetAsync(...).Result; // <-- I really don't care if this is asynchronous or not
return new HttpUserContext(result);
}
Message loop example:
var mq = new MessageQueue();
// we then run say 8 tasks that do this
for (;;)
{
var m = mq.Get();
var c = GetCommand(m);
c.InvokeAsync().Wait();
m.Delete();
}
When you have a message loop that allow things to happen in parallel and you have asynchronous methods, there's a opportunity to minimize latency. Basically, what I want to accomplish in this instance is to minimize latency and idle time. Though I'm actually unsure as to how to invoke into the command that's associated with the message that arrives off the queue.
To be more specific, if the command invocation needs to do service requests there's going to be latency in the invocation that could be used to get the next message. Stuff like that. I can totally do this simply by wrapping up things in queues and coordinating this myself but I'd like to see this work with just some async/await stuff.
While I could make most of my methods async I don't feel as if I have a basic understanding of when I'd must do this.
Start at the lowest level. It sounds like you've already got a start, but if you're looking for more at the lowest level, then the rule of thumb is anything I/O-based should be made async (e.g., HttpClient).
Then it's a matter of repeating the async infection. You want to use async methods, so you call them with await. So that method must be async. So all of its callers must use await, so they must also be async, etc.
how would I then invoke the first async method of my program if I built say a message loop where I want some control of the degree of parallelism?
It's easiest to put the framework in charge of this. E.g., you can just return a Task<T> from a WebAPI action, and the framework understands that. Similarly, UI applications have a message loop built-in that async will work naturally with.
If you have a situation where the framework doesn't understand Task or have a built-in message loop (usually a Console application or a Win32 service), you can use the AsyncContext type in my AsyncEx library. AsyncContext just installs a "main loop" (that is compatible with async) onto the current thread.
Since the HttpClient doesn't have any synchronous methods, what can I safely presume to do if I have an abstraction that isn't async aware?
The correct approach is to change the abstraction. Do not attempt to block on asynchronous code; I describe that common deadlock scenario in detail on my blog.
You change the abstraction by making it async-friendly. For example, change IUserContext IUserContextService.GetUserContext() to Task<IUserContext> IUserContextService.GetUserContextAsync().
I've read about the ConfigureAwait(false) but I don't really understand what it does. It's strange to me that it's set after the async invocation.
You may find my async intro helpful. I won't say much more about ConfigureAwait in this answer because I think it's not directly applicable to a good solution for this question (but I'm not saying it's bad; it actually should be used unless you can't use it).
Just bear in mind that async is an operator with precedence rules and all that. It feels magical at first, but it's really not so much. This code:
var result = await httpClient.GetAsync(url).ConfigureAwait(false);
is exactly the same as this code:
var asyncOperation = httpClient.GetAsync(url).ConfigureAwait(false);
var result = await asyncOperation;
There are usually no race conditions in async code because - even though the method is asynchronous - it is also sequential. The method can be paused at an await, and it will not be resumed until that await completes.
When you have a message loop that allow things to happen in parallel and you have asynchronous methods, there's a opportunity to minimize latency.
This is the second time you've mentioned a "message loop" "in parallel", but I think what you actually want is to have multiple (asynchronous) consumers working off the same queue, correct? That's easy enough to do with async (note that there is just a single message loop on a single thread in this example; when everything is async, that's usually all you need):
await tasks.WhenAll(ConsumerAsync(), ConsumerAsync(), ConsumerAsync());
async Task ConsumerAsync()
{
for (;;) // TODO: consider a CancellationToken for orderly shutdown
{
var m = await mq.ReceiveAsync();
var c = GetCommand(m);
await c.InvokeAsync();
m.Delete();
}
}
// Extension method
public static Task<Message> ReceiveAsync(this MessageQueue mq)
{
return Task<Message>.Factory.FromAsync(mq.BeginReceive, mq.EndReceive, null);
}
You'd probably also be interested in TPL Dataflow. Dataflow is a library that understands and works well with async code, and has nice parallel options built-in.
While I appreciate the insight from community members it's always difficult to express the intent of what I'm trying to do but tremendously helpful to get advice about circumstances surrounding the problem. With that, I eventually arrived that the following code.
public class AsyncOperatingContext
{
struct Continuation
{
private readonly SendOrPostCallback d;
private readonly object state;
public Continuation(SendOrPostCallback d, object state)
{
this.d = d;
this.state = state;
}
public void Run()
{
d(state);
}
}
class BlockingSynchronizationContext : SynchronizationContext
{
readonly BlockingCollection<Continuation> _workQueue;
public BlockingSynchronizationContext(BlockingCollection<Continuation> workQueue)
{
_workQueue = workQueue;
}
public override void Post(SendOrPostCallback d, object state)
{
_workQueue.TryAdd(new Continuation(d, state));
}
}
/// <summary>
/// Gets the recommended max degree of parallelism. (Your main program message loop could use this value.)
/// </summary>
public static int MaxDegreeOfParallelism { get { return Environment.ProcessorCount; } }
#region Helper methods
/// <summary>
/// Run an async task. This method will block execution (and use the calling thread as a worker thread) until the async task has completed.
/// </summary>
public static T Run<T>(Func<Task<T>> main, int degreeOfParallelism = 1)
{
var asyncOperatingContext = new AsyncOperatingContext();
asyncOperatingContext.DegreeOfParallelism = degreeOfParallelism;
return asyncOperatingContext.RunMain(main);
}
/// <summary>
/// Run an async task. This method will block execution (and use the calling thread as a worker thread) until the async task has completed.
/// </summary>
public static void Run(Func<Task> main, int degreeOfParallelism = 1)
{
var asyncOperatingContext = new AsyncOperatingContext();
asyncOperatingContext.DegreeOfParallelism = degreeOfParallelism;
asyncOperatingContext.RunMain(main);
}
#endregion
private readonly BlockingCollection<Continuation> _workQueue;
public int DegreeOfParallelism { get; set; }
public AsyncOperatingContext()
{
_workQueue = new BlockingCollection<Continuation>();
}
/// <summary>
/// Initialize the current thread's SynchronizationContext so that work is scheduled to run through this AsyncOperatingContext.
/// </summary>
protected void InitializeSynchronizationContext()
{
SynchronizationContext.SetSynchronizationContext(new BlockingSynchronizationContext(_workQueue));
}
protected void RunMessageLoop()
{
while (!_workQueue.IsCompleted)
{
Continuation continuation;
if (_workQueue.TryTake(out continuation, Timeout.Infinite))
{
continuation.Run();
}
}
}
protected T RunMain<T>(Func<Task<T>> main)
{
var degreeOfParallelism = DegreeOfParallelism;
if (!((1 <= degreeOfParallelism) & (degreeOfParallelism <= 5000))) // sanity check
{
throw new ArgumentOutOfRangeException("DegreeOfParallelism must be between 1 and 5000.", "DegreeOfParallelism");
}
var currentSynchronizationContext = SynchronizationContext.Current;
InitializeSynchronizationContext(); // must set SynchronizationContext before main() task is scheduled
var mainTask = main(); // schedule "main" task
mainTask.ContinueWith(task => _workQueue.CompleteAdding());
// for single threading we don't need worker threads so we don't use any
// otherwise (for increased parallelism) we simply launch X worker threads
if (degreeOfParallelism > 1)
{
for (int i = 1; i < degreeOfParallelism; i++)
{
ThreadPool.QueueUserWorkItem(_ => {
// do we really need to restore the SynchronizationContext here as well?
InitializeSynchronizationContext();
RunMessageLoop();
});
}
}
RunMessageLoop();
SynchronizationContext.SetSynchronizationContext(currentSynchronizationContext); // restore
return mainTask.Result;
}
protected void RunMain(Func<Task> main)
{
// The return value doesn't matter here
RunMain(async () => { await main(); return 0; });
}
}
This class is complete and it does a couple of things that I found difficult to grasp.
As general advice you should allow the TAP (task-based asynchronous) pattern to propagate through your code. This may imply quite a bit of refactoring (or redesign). Ideally you should be allowed to break this up into pieces and make progress as you work towards to overall goal of making your program more asynchronous.
Something that's inherently dangerous to do is to call asynchronous code callously in an synchronous fashion. By this we mean invoking the Wait or Result methods. These can lead to deadlocks. One way to work around something like that is to use the AsyncOperatingContext.Run method. It will use the current thread to run a message loop until the asynchronous call is complete. It will swap out whatever SynchronizationContext is associated with the current thread temporarily to do so.
Note: I don't know if this is enough, or if you are allowed to swap back the SynchronizationContext this way, assuming that you can, this should work. I've already been bitten by the ASP.NET deadlock issue and this could possibly function as a workaround.
Lastly, I found myself asking the question, what is the corresponding equivalent of Main(string[]) in an async context? Turns out that's the message loop.
What I've found is that there are two things that make out this async machinery.
SynchronizationContext.Post and the message loop. In my AsyncOperatingContext I provide a very simple message loop:
protected void RunMessageLoop()
{
while (!_workQueue.IsCompleted)
{
Continuation continuation;
if (_workQueue.TryTake(out continuation, Timeout.Infinite))
{
continuation.Run();
}
}
}
My SynchronizationContext.Post thus becomes:
public override void Post(SendOrPostCallback d, object state)
{
_workQueue.TryAdd(new Continuation(d, state));
}
And our entry point, basically the equivalent of an async main from synchronous context (simplified version from original source):
SynchronizationContext.SetSynchronizationContext(new BlockingSynchronizationContext(_workQueue));
var mainTask = main(); // schedule "main" task
mainTask.ContinueWith(task => _workQueue.CompleteAdding());
RunMessageLoop();
return mainTask.Result;
All of this is costly and we can't just go replace calls to async methods with this but it does allow us to rather quickly create the facilities required to keep writing async code where needed without having to deal with the whole program. It's also very clear from this implementation where the worker threads go and how the impact concurrency of your program.
I look at this and think to myself, yeap, that's how Node.js does it. Though JavaScript does not have this nice async/await language support that C# currently does.
As an added bonus, I have complete control of the degree of parallelism, and if I want, I can run my async tasks completely single threaded. Though, If I do so and call Wait or Result on any task, it will deadlock the program because it will block the only message loop available.

Converting Synchronous code to Asynchronous code in C#

I am trying to make a method of mine into something that can be called asynchronously.
Normally from the AddQueue Method, I would just call the ListOfJobsInQueue methods in the WorkingSession class, get its result and be done with it.
Using what info I could find regarding Async programming on here, I have done up the below code, but it seems to be getting stuck on the CurrentPageCode property call.
It does not even get to the MessageBox.Show("Processing complete with " + queueResult.Count + " rows"); line.
Could someone please assist and show me where I'm going wrong?
//Primary Class
public void AddQueue()
{
MessageBox.Show(GetJobsFromQueueAsync().Result.Count().ToString());
}
async Task<List<string>> GetJobsFromQueueAsync()
{
Task<List<string>> getJobsTask = WorkingSession.GetlistOfJobsAsync();
List<string> queueResult = await getJobsTask;
MessageBox.Show("Processing complete with " + queueResult.Count + " rows");
return queueResult;
}
//***
//WorkingSession Class
public Task<List<string>> GetlistOfJobsAsync()
{
return Task.Run<List<string>>(() =>
{
return ListOfJobsInQueue();
});
}
public List<string> ListOfJobsInQueue()
{
if (CurrentPageCode == "CS1")
{
List<string> actionList = new List<string>();
short pageNum = PageCurrent;
short pageMax = PageMax;
for (short atPage = pageNum; atPage <= pageMax; atPage++)
{
//Scan each job on the current queue page
for (int lineNum = 5; lineNum < 18; lineNum++)
{
string reference = GetJobText(new Coordinate { row = lineNum });
actionList.Add(reference);
}
//Once finished with this job page, goto the next
SendCMDKey(Mnemonic.F8);
}
return actionList;
}
else
{
return null;
}
}
//Other method / property signatures (for reference)
public string CurrentPageCode;
public bool SendCMDKey(Mnemonic command)
public string GetJobText(Coordinate coordinate)
//***
The deadlock problem is actually this method:
public void AddQueue()
{
MessageBox.Show(GetJobsFromQueueAsync().Result.Count().ToString());
}
Calling Task.Wait or Task<T>.Result should be avoided in async code. I explain the deadlock in full on my blog, but the summary version is that await will capture a context (in this case, the UI context) and attempt to resume its async method on that context (in this case, on the UI thread). With some contexts (e.g., the UI context), if you block a thread in that context (e.g., calling Task<T>.Result on the UI thread), then the async method cannot resume on that context, causing a deadlock.
To fix it, use async all the way:
public async Task AddQueueAsync()
{
var jobs = await GetJobsFromQueueAsync();
MessageBox.Show(jobs.Count().ToString());
}
This code is also not ideal, though in a much more subtle way:
public Task<List<string>> GetlistOfJobsAsync()
{
return Task.Run<List<string>>(() =>
{
return ListOfJobsInQueue();
});
}
By wrapping an entire method's logic in Task.Run, what you're really doing is writing a "fake asynchronous" method. It's got an asynchronous signature but the logic is just synchronous work on a background thread.
It's best to push any Task.Run use as far towards the UI layer as possible; keep it out of any reusable library methods. Make your APIs tell the truth: have synchronous signatures for synchronous work. I have a blog series that goes into detail.
The simplest that I can
public async Task<int> GetWorkFlowStageAsync(string tracker,CancellationToken? token = null)
{
return await Task.FromResult(0);
}

How to track if an async/awaitable task is running

I'm trying to transition from the Event-based Asynchronous Pattern where I tracked running methods using unique id's and the asynoperationmanager. As this has now been dropped from Windows 8 Apps I'm trying to get a similar effect with Async/Await but can't quite figure out how.
What I'm trying to achieve is something like
private async Task updateSomething()
{
if(***the method is already running***)
{
runagain = true;
}
else
{
await someMethod();
if (runagain)
{
run the method again
}
}
}
The part I'm struggling with is finding out if the method is running. I've tried creating a Task and looking at the status of both that and the .status of the async method but they don't appear to be the correct place to look.
Thanks
UPDATE: This is the current code I use in .net 4 to achieve the same result. _updateMetaDataAsync is a class based on the Event-Based Asynchronous Pattern.
private void updateMetaData()
{
if (_updateMetaDataAsync.IsTaskRunning(_updateMetaDataGuid_CheckAllFiles))
{
_updateMetaDataGuid_CheckAllFiles_Again = true;
}
else
{
_updateMetaDataGuid_CheckAllFiles_Again = false;
_updateMetaDataAsync.UpdateMetaDataAsync(_updateMetaDataGuid_CheckAllFiles);
}
}
private void updateMetaDataCompleted(object sender, UpdateMetaDataCompletedEventArgs e)
{
if (_updateMetaDataGuid_CheckAllFiles_Again)
{
updateMetaData();
}
}
async/await itself is intended to be used to create sequential operations executed asynchronously from the UI thread. You can get it to do parallel operations, but generally the operations "join" back to the UI thread with some sort of result. (there's also the possibility of doing "fire-and-forget" types of asynchronous operations with await but it's not recommended). i.e. there's nothing inherent to async/await to support progress reporting.
You can get progress out of code using async/await; but you need to use new progress interfaces like IProgress<T>. For more info on progress reporting with async/await, see http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx. Migrating to this should just be a matter of calling an IProgress delegate instead of a Progress event.
If you're using a Task you've created, you can check the Task's Status property (or just see Task.IsCompleted if completion is the only state you are interested in).
That being said, await will not "return" until the operation either completes, raises an exception, or cancels. You can basically safely assume that, if you're still waiting on the "await", your task hasn't completed.
SemaphoreSlim queueToAccessQueue = new SemaphoreSlim(1);
object queueLock = new object();
long queuedRequests = 0;
Task _loadingTask;
public void RetrieveItems() {
lock (queueLock) {
queuedRequests++;
if (queuedRequests == 1) { // 1 is the minimum size of the queue before another instance is queued
_loadingTask = _loadingTask?.ContinueWith(async () => {
RunTheMethodAgain();
await queueToAccessQueue.WaitAsync();
queuedRequests = 0; // indicates that the queue has been cleared;
queueToAccessQueue.Release()
}) ?? Task.Run(async () => {
RunTheMethodAgain();
await queueToAccessQueue.WaitAsync();
queuedRequests = 0; // indicates that the queue has been cleared;
queueToAccessQueue.Release();
});
}
}
}
public void RunTheMethodAgain() {
** run the method again **
}
The added bonus is that you can see how many items are sitting in the queue!

Categories