Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have some code that is using Task<T> which defers returning a result from a serial read operation for a short time, like this:

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 behind this code is to return the result when no new characters have arrived for a specified interval.

However, due to factors outside of my control, I must use .NET 3.5, which prevents me using Task<T>, so I have to refactor this code somehow.

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

Clarification

Although the specific code I showed happens to be a timed delay, my usage isn't limited to delaying things. There may be other cases where I want to start some 'long running' polling task immediately. A typical situation would be an I/O bound operation, for example something the periodically queries a device attached to the serial port and then raises an event when some condition is met.

like image 642
Tim Long Avatar asked Mar 19 '14 19:03

Tim Long


People also ask

What is difference between Task and thread in C#?

Differences Between Task And ThreadThe Thread class is used for creating and manipulating a thread in Windows. A Task represents some asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel. The task can return a result.

What is TaskCompletionSource C#?

While Task. Run turns something synchronous into a Task (by running it on a separate thread), TaskCompletionSource turns something that is already asynchronous into a Task .

How do I create a new Task in C#?

To start a task in C#, follow any of the below given ways. Use a delegate to start a task. Task t = new Task(delegate { PrintMessage(); }); t. Start();

Does Task run block thread?

Run is misused to run IO blocking tasks. Although the code will work just fine (e.g UI not not freeze) but it is still a wrong approach. This is because Task. Run will still block a thread from thread pool the entire time until it finishes the method.


3 Answers

Use a Timer (which is actually how Delay is implemented 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);
}

To your edit, if you're using an asynchronous application built on top of asynchronous IO, then that asynchronous IO will already expose some method of asynchrony. It could be an event based model, it could accept a callback, it could be using IAsyncResult, etc. Task is yet another possible approach to asynchronous programming, and you're certainly capable of translating any approach to any other approach, if one is preferable to you, but generally people tend to stick with whatever method the underlying IO they are performing uses, unless they have some compelling reason to do otherwise.

like image 114
Servy Avatar answered Nov 02 '22 22:11

Servy


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

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

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

like image 32
Sam Harwell Avatar answered Nov 03 '22 00:11

Sam Harwell


Although the specific code I showed happens to be a timed delay, my usage isn't limited to delaying things. There may be other cases where I want to start some 'long running' polling task immediately. A typical situation would be an I/O bound operation, for example something the periodically queries a device attached to the serial port and then raises an event when some condition is met.

A notable and handy hack for coding such scenarios with C# 2.0 - 4.0 is to use self-driven IEnumerable and yield. It allows to implement an asynchronous state machine, similar to async/await of C# 5.0. This way you keep the convenient linear code flow for your asynchronous logic. All C# language code control statements work (besides you can't do yield return from inside try/catch).

For example, a console app 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 could be wrapped with a handler which would call nextStep, similar to the above timer callback. The code would continue after the corresponding yield return, upon the event.

There are quite a few implementations taking advantage of this approach, e.g., Jeffrey Richter's AsyncEnumerator.

like image 35
noseratio Avatar answered Nov 02 '22 23:11

noseratio