How can I get the equivalent of Task <T> in .net 3.5?

I have code that uses Task<T> that interrupts the return of a result from a sequential read operation for a short time, for example:

 void ReturnResponseAfterAShortDelay() { if (delayedResponseCancellationTokenSource != null) delayedResponseCancellationTokenSource.Cancel(); // Cancel any pending operations and start a new one. delayedResponseCancellationTokenSource = new CancellationTokenSource(); log.InfoFormat("Deferring response for {0} ms", Settings.Default.TimeoutMs); Task.Delay(Properties.Settings.Default.TimeoutMs, delayedResponseCancellationTokenSource.Token) .ContinueWith((continuation) => ReturnWhateverHasArrived(), TaskContinuationOptions.NotOnCanceled) .Start(); } 

The idea of ​​this code is to return the result when new characters have not arrived for the specified interval.

However, due to factors beyond my control, I have to use .NET 3.5, which prevents me from using Task<T> , so I need to somehow reorganize this code.

How can I achieve the same result without using Task<T> ?

Explanation

Although the specific code I showed is a time delay, my use is not limited to the delay. There may be other occasions when I want to immediately start the "long time" task. A typical situation is an I / O binding operation, for example, something periodically requests a device connected to a serial port, and then triggers an event when a condition is met.

+9
c # asynchronous task-parallel-library task
source share
6 answers

Although the specific code I showed is a time delay, my use is not limited to the delay. There may be other occasions when I want to immediately begin the "long time" task. a typical situation is an I / O binding operation, for example, something periodically requests a device connected to a serial port, and then triggers an event when a condition is met.

A noticeable and convenient hack for coding such scenarios with C # 2.0 - 4.0 is the use of self-driven IEnumerable and yield . It allows you to implement an asynchronous state machine similar to async/await for C # 5.0. This way you maintain a convenient linear code stream for your asynchronous logic. All C # language code control statements work (in addition, you cannot do yield return from inside try/catch ).

For example, a console application with a timer:

 using System; using System.Collections; using System.Threading; namespace ConsoleApplication_22516303 { class Program { class AsyncLogic { public EventHandler Completed = delegate { }; IEnumerable WorkAsync(Action nextStep) { using (var timer = new System.Threading.Timer(_ => nextStep())) { timer.Change(0, 500); var tick = 0; while (tick < 10) { // resume upon next timer tick yield return Type.Missing; Console.WriteLine("Tick: " + tick++); } } this.Completed(this, EventArgs.Empty); } public void Start() { IEnumerator enumerator = null; Action nextStep = () => enumerator.MoveNext(); enumerator = WorkAsync(nextStep).GetEnumerator(); nextStep(); } } static void Main(string[] args) { var mre = new ManualResetEvent(false); var asyncLogic = new AsyncLogic(); asyncLogic.Completed += (s, e) => mre.Set(); asyncLogic.Start(); mre.WaitOne(); Console.WriteLine("Completed, press Enter to exit"); Console.ReadLine(); } } } 

Any event can be wrapped by a handler that will call nextStep , similar to the previous timer callback. The code will continue after the corresponding yield return , after the event.

There are quite a few implementations that use this approach, such as Jeffrey Richter AsyncEnumerator .

+4
source share

You can use any of the following packages available through NuGet:

  • You can use the TaskParallelLibrary package on NuGet. This package has a strong name and is the back-port.NET 3.5 of the parallel .NET Task library for .NET 4, which was included in Reactive Extensions.

  • You can use the System.Threading.Tasks.Unofficial package on NuGet. This alternative implementation uses the Mono code base instead of the Microsoft implementation. Please note that the assembly contained in this package does not have a strong name, so if your library uses strong names, this is not an option.

+17
source share

Use a Timer (which actually implements Delay internally).

 private static HashSet<Timer> timers = new HashSet<Timer>(); public static void ExecuteAfter(Action action, TimeSpan delay) { Timer timer = null; timer = new System.Threading.Timer(s => { action(); timer.Dispose(); lock (timers) timers.Remove(timer); }, null, (long)delay.TotalMilliseconds, Timeout.Infinite); lock (timers) timers.Add(timer); } 

In your editing, if you are using an asynchronous application built on top of asynchronous I / O, then this asynchronous IO already yields some asynchronous method. It can be an event-based model, it can take a callback, it can use IAsyncResult , etc. Task is another possible approach to asynchronous programming, and you can certainly translate any approach to any other approach if you prefer you, but as a rule, people tend to stick to any method that uses the basic IO that they perform if they have no good reason for this.

+3
source share

Based on @Servy's answer, slightly modified, which worked for me:

 ExecuteAfter(() => MessageBox.Show("hi"), 1000 ); 

The code:

 public static void ExecuteAfter(Action action, int milliseconds) { System.Threading.Timer timer = null; timer = new System.Threading.Timer(s => { action(); timer.Dispose(); lock (timers) timers.Remove(timer); }, null, milliseconds, UInt32.MaxValue-10); lock (timers) timers.Add(timer); } private static HashSet<System.Threading.Timer> timers = new HashSet<System.Threading.Timer>() 
0
source share

I don't have enough reputation to respond to @ T.Todua directly, but his answer was exactly what I needed and worked great!

I was able to use it unchanged and just changed the task. He did not block and consistently worked. He is currently exhibiting in commercial development ...

eg

ExecuteAfter(() => performDelayedTask(), 5000);

0
source share

Use ThreadPool to execute the method:

http://msdn.microsoft.com/en-us/library/kbf0f1ct(v=vs.110).aspx

Edit for Servy:

The default scheduler for the parallel task library and PLINQ uses the .NET Framework ThreadPool to queue and do the work. In the .NET Framework 4, ThreadPool uses the information provided by the System.Threading.Tasks.Task type to effectively support the fine-grained parallelism (short-term units of work) that parallel tasks and queries often represent.

Edit 2 (due to negative feedback):

This code does exactly what the OP wants:

  void ReturnResponseAfterAShortDelay(WaitHandle cancellationToken) { log.InfoFormat("Deferring response for {0} ms", Settings.Default.TimeoutMs); ThreadPool.RegisterWaitForSingleObject(cancellationToken, () => { if (!cancellationToken.WaitOne(0)) { ReturnWhateverHasArrived(); } }, null, Settings.Default.TimeoutMs, true); } 
-2
source share

All Articles