Click here to Skip to main content
Click here to Skip to main content

Android Inspired C# AsyncTask

, 7 Mar 2013
Rate this:
Please Sign up or sign in to vote.
Inspired by Android asynchronous callback mechanism for short lived background tasks.

Motivation and Background

Lately I was getting agitated from the tedious callback pattern entrenched in the .NET Framework, most notably WinForms. Being far from pro, but still familiar with the the Android Java SDK, I found the async callback mechanism there enriching. It is simple, expressive yet powerful for the task of decoupling execution from the GUI thread. I naturally got inspired and having to do some WinForms job again, I firmly decided on coming with my own copycat in C#. From the official documentation we can see the description:

“AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers. AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor, and FutureTask. An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate, and onPostExecute.”

From the same page we can see a brief example of asynchronous file download intended to be used directly from a GUI event handler. Instead I will show the same snippet but used in the form of anonymous class, which is more commonly found in use by Android developers.

new AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
 
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
 
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}.Execute("www.fileserver.com/filename.ext");

Pushing the Limits of C#

While trying to come up with an exact copy, following the semantics of the Java implementation I was pushing my C# skills to the maximum. At first, quite naively, I was hoping I could come with a nice abstraction in the form of IAsyncTask generic interface. However, I quickly realized there isn’t really such thing as an anonymous classes in C#, but rather the notion of anonymous types in the form of object initializers. The subtle difference being the limitation that you can initialize only properties and fields and no way to override/redefine a function.

The following snippet demonstrates the issue:

class MyClass<T>
{
    public T MyProp { get; set; },
    public T GetMyProp() { return MyProp; }
}
 
var myvar = new MyClass<int>()
{
  MyProp = 1,
  int GetMyProp() { return -666 }
}

Another limitation in my opinion is the lack of support of void type as a valid generic type. Ideally I would like to do:

class MyClass<T1,T2> : MyBaseClass<T1,void,T3>
{}
//then i can check for typeof(T2) is void ?

Here I deleted a paragraph for the simple reason I did not want to turn the article into a rant, but rather I decided to share only one link, which I found most hilarious from all: (why void is not allowed in generics

Anyway, as a result, I ended up with duplicating code. Not really a stopper, but something I would ultimately love to get rid of. 

Final Realization

Haven’t I stumbled on the following article, by DaveyM69: GenericBackgroundWorker. I might not even had the nerves left to write an entire article. However, having spent a weekend time all together of putting some working code and the WinForms demo project together, I felt simply obliged.

Since, C# let us dynamically initialize nothing else but field members and properties, the only sensible option I could think of was delegates exposed through properties.

My initial implementation is in the form of a simple AsyncTask with three callback functions defined: Pre, Do, and Post with two generic types Tin as parameter to Do and Tout as return value from Do and input parameter to Post.

public class AsyncTask<Tin, Tout>{
 
   private BackgroundWorker<Tin, int, Tout> bw;//cannot pass void :(
   public delegate bool PreFunc();
   public delegate Tout DoFunc(Tin input);
   public delegate void PostFunc(Tout output, Exception err);

   public PreFunc Pre
   {
       get;
       set;
   }

   public DoFunc Do
   {
       get;
       set;
   }

   public PostFunc Post
   {
       get;
       set;
   }

   public AsyncTask()
   {
       this.Pre = delegate { return true; };

       this.bw = new BackgroundWorker<Tin, int, Tout>();

       this.bw.DoWork += new EventHandler<DoWorkEventArgs<Tin, Tout>>(OnDoWork);

       this.bw.RunWorkerCompleted += 
         new EventHandler<RunWorkerCompletedEventArgs<Tout>>(OnRunWorkerCompleted);
   }
 
   public void Execute(Tin input)
   {
       if (Pre())
       {
           this.bw.RunWorkerAsync(input);
       }
   }

   private void OnDoWork(object sender, DoWorkEventArgs<Tin, Tout> e)
   {
       Tout r = Do(e.Argument);
       e.Result = r;
   }

   private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs<Tout> e)
   {
       Post(e.Result, e.Error);
   }
}

Few things to notice about this sample code. Both Do and Post delegates needs to be defined, thus required, where Pre is entirely optional. I have achieved this by creating an anonymous function returning true in the constructor. The Pre function is meant as a precondition, returning only true in case we allow further execution.

The rest of the implementation is pretty straightforward, using the GenericBackgroundWorker as you would normally use the native BackgroundWorker.

For my later implementation I wanted to support progress reporting as well. That required a reference to AsyncTask from within the anonymous Do function, so that I can report the progress to the underlying BackgroundWorker member. Not something a small indirection with tiny wrapper can’t solve.

Use and Purpose

The sole intended purpose of the AsyncTask is to run any GUI long-term process in a background thread. Although, the use cases are not limited to GUI, but anywhere you require seamless decoupling using IoC between two threads. In my opinion a cleaner and less complicated solution in contrast to some famous patterns, like publish-subscribe pattern. By abstaining from using any C# style events we had cut on plenty of formalities and code size as result. But what I find most compelling about this solution is the clean intent of use that it demonstrate and at the same time somehow elegantly maintain separation of concerns for us.

In case you are not familiar with the C# lambda function expressions, you better check first the MSDN. There exist various ways with plenty of syntax sugar around readily available to you. At first the lambda syntax looked daunting to me. Disconnected from what I would consider classical C#, but you will end up loving the power it gives you above all.

new AsyncTask<int, int, string>(){
 
   Pre = delegate
   {
       return true;
   },

   Go = progress =>
   {
       this.progressBar1.Value = progress;
   },

   Do = (input, progress) =>
   {
           progrss.Report(1);
           Thread.Sleep(1000);
           progrss.Report(100);
           return “it took us 1 sec to finish”;
   },

   Post = (output, err) =>
   {
        //handle err
        //print output
   }

}.Execute(Tin);

You can use any type for the generic parameters, however I recommend using immutable value-types. The worst situation you might find yourself is sharing a reference between the background threads and running into well known plethora of threading issues. By rule of thumb you should keep the memory access confined to the local scope of the Do function.

A final word of advice is to consider the fact that the same limitation stated in the Android documentation hold true in our current implementation too. That is, you should use AsyncTask only for short-lived async operations. It has to do with the fact that the GenericBackgroundWorker helper class utilizes the system thread-pool, in accordance with the original BackroundWorker. Otherwise you risk starving the thread pool and as result defer execution of any further GUI actions in the process.

The WinForms Demo

The accompanying demo application constitutes a simple WinForms demo form with: a button to spawn the threads, a dropdown to specify number of threads and textbox to report misc. output . An overview of the form you can see in the beginning of this article.

Please make note of the define statement in the beginning of TestForm.cs, #define ADVANCED. By simply commenting you can switch between the simple example of using AsynTask to the more advanced one AsyncTask with progress report.

#define ADVANCED
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 

namespace AsyncTaskCallback
{

    public partial class TestForm : Form
    {
        public TestForm()
        {
            InitializeComponent();
 
            this.progressBar1.Value = 0;
            this.progressBar1.Maximum = 0;
        }
 
        private void OnGoClick(object sender, EventArgs e)
        {
            int count = int.Parse((string)(this.comboBox1.SelectedItem ?? "1"));
 
            this.progressBar1.Maximum += count;
 
            int i;
            for (i = 1; i <= count; i++)
            {
#if !ADVANCED
               new AsyncTask<int, string>()
                    {
                        Do = input => { 
                            int timeout = RandomRoulette();
                            Thread.Sleep(timeout);
                            
                            return string.Format("T{0} returned after {1} secs",input,timeout/1000);
                        },
                        
                        Post = (output, err) => { 
                            PrintInTheGUI(output); }
 
                    }.Execute(i);//TODO send something meaningful
#endif
 
#if ADVANCED
                new AsyncTask<int, int, string>()
                {
                    //pre-condition, first function to  e called
                    Pre = delegate
                    {
                        return true;
                    },
 
                    //here you have the chance to report progress back to the GUI
                    Go = progress =>
                    {
                        this.progressBar1.Value += 1;//usually = progress; 
                    },
 
                    //this function is executed asynchronously in another thread
                    Do = (input, progress) =>
                    {
                        int timeout = RandomRoulette();
                        Thread.Sleep(timeout);
                        progress.Report(100, input);
                        return string.Format("T{0} returned after {1} secs", input, timeout / 1000);
                    },
 
                    //the result of the asynchronous task or exception during its execution
                    Post = (output, err) =>
                    {
                        PrintInTheGUI(output);
                    }
 
                }.Execute(i);//invoke the task and pass input parameter
#endif
 
            }
 
            //this will be the first thing you will see after pressing the button
            this.PrintInTheGUI("Threads spawned :" + (i - 1).ToString());
        }
 
        private static int RandomRoulette()
        {
            int timeout = new Random().Next(1, 13) * 1000;
 
            return timeout;
        }
 
        private void PrintInTheGUI(string text)
        {
            this.textBox1.Text = text + System.Environment.NewLine + this.textBox1.Text;
        }
    }
}

Just for experimental purposes you might try using the “PrintOnGUIThread” function inside the Do function and see what the effects will be. You will get an IllegalOperationException when trying to access an element of the GUI from outside the main GUI thread. The intended issue this article addresses.

Also, notice the visibility of the other function, which otherwise we can safely call from separate thread. Namely, the static function: RandomRoulette(). Any static function is guaranteed to be thread safe on the CLR. The function itself returns the sleep timeout in milliseconds, at random anyway in the range of 1 to 13 seconds.

In the output textbox, first thing after pressing Start will be the print-out of how many threads we have requested. For a good reason I used the word requested and not spawned, since the threads get scheduled on the threadpool first and executed no more than X at time. Where X really depends on the number of async request you demand, leave it to the OS to figure it out. In case you are curious you can always pause execution in the debugger and have a look at the Threads window pane in Visual Studio 10. While there, you might as well enjoy the wonderful parallel stack provided. Well done VS Wink | <img src=

I could have easily disabled the Start Button once you invoke a single batch of async requests, however I made a small adaptation so that it is safe to request further batches, feel free to experiment a bit. Let me know if you encounter any runtime errors.

I guess that’s it. The self of the code is pretty straightforward to follow. 

Realization based on TPL

Due to popular demand Smile | <img src=  Here comes the same Contract based AsynTask concept implemented using TPL. Task Parallel Library is a general purpose threading library living in the System.Threading.Tasks namespace. It's goal is to simplify concurrent programming, while taking care of low level concerns as scheduling, state management etc. A good document (TAP.docx) can be find on MSDN giving a comprehensive overview of what it is all about together with usage examples. A more insights of the internal implementation you can find on the following blog post, in case you are really curious to peek under the hood. 

I recommend first having some background, before jumping to new AsyncTask implementation below.

public class AsyncTask<Tin, Tgo, Tout> where Tgo : struct
{
    public class AsyncTaskCallBack : IProgress<Tgo>
    {
        private readonly AsyncTask<Tin, Tgo, Tout> asyncTask;

        public AsyncTaskCallBack(AsyncTask<Tin, Tgo, Tout> at)
        {
            asyncTask = at;
        }
        public void ThrowIfCancel()
        {
            this.asyncTask.cancelToken.ThrowIfCancellationRequested();
        }
        public bool IsCancellationRequested
        {
            get
            {
                return this.asyncTask.cancelToken.IsCancellationRequested;
            }
        }
        public void Report(Tgo value)
        {
            //make sure we are on the caller thread
            this.asyncTask.syncContext.Post(o => this.asyncTask.Go(value), null);
        }

    }

    private readonly SynchronizationContext syncContext;
    private readonly TaskCreationOptions taskCreateOptions;
    private readonly CancellationToken cancelToken;
    private readonly AsyncTaskCallBack callback;
    private Task<Tout> task;

    /// <summary>
    /// This function is executed before anything.
    /// </summary>
    public Func<bool> Pre;

    /// <summary>
    /// The code inside this function is executed on ananother thread.
    /// </summary>
    public Func<Tin, AsyncTaskCallBack, Tout> Do;

    /// <summary>
    /// Do your GUI progress control manipulatio here
    /// </summary>
    public Action<Tgo> Go;

    /// <summary>
    /// Post processing any result
    /// </summary>
    public Action<Tout> Post;

    /// <summary>
    /// Called upon an unhandled exception. If return true, exception is propagted up the stack.
    /// </summary>
    public Func<Exception, bool> Err;

    public AsyncTask(CancellationToken ct)
        : this()
    {
        cancelToken = ct;
    }
    public AsyncTask(TaskCreationOptions tco = TaskCreationOptions.None)
    {
        //make it optional
        this.Pre = delegate { return true; };
        this.Err = delegate(Exception e) { return true; };

        this.callback = new AsyncTaskCallBack(this);
        this.taskCreateOptions = tco;
        this.syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
    }
    public async void Execute(Tin input)
    {
        if (Pre())
        {
            Tout result = default(Tout);

            try
            {
                result = await RunTaskAsync(input);

                Post(result);
            }
            catch (OperationCanceledException oce)
            {
                if (Err(oce))
                    throw;
            }
            catch (AggregateException aex)
            {
                if (Err(aex.Flatten()))
                    throw;
            }
            catch (Exception ex)
            {
                if (Err(ex))
                    throw;
            }
        }
    }

    private Task<Tout> RunTaskAsync(Tin input)
    {
        this.task = Task.Factory.StartNew(() => Do(input, callback),
            cancelToken,
            taskCreateOptions,
            TaskScheduler.Default);

        return task;
    }

    /// <summary>
    /// Winforms you need to use Control.Invoke Method (Delegate)
    /// to make sure that control is updated in the UI thread.
    /// </summary>
    /// <param name="ctrl"></param>
    /// <param name="action"></param>
    private static void PerformInvoke(Control ctrl, Action action)
    {
        if (ctrl.InvokeRequired)
            ctrl.Invoke(action);
        else
            action();
    }
}

I will omit examining what it takes to come with async solution in C#, but rather focus on how it servers our purpose. As I have already mentioned, TPL is general purpose library, while the AsyncTask proposed in this article is very specialized with one goal in mind, to decouple the GUI thread from the rest of your app. At the same time gives you a clear and safe Contract based implementation with no side effects.

First thing to notice is the very compact solution I came up with. I have introduced the predefined generic delegates, Action and Func, which come as part of the latest dotNet. The Execute method is marked async, which awaits on the RunTaskAsync method. Those two keywords async and awaits are the core of what constitutes a basic TPL solution. Another subtle difference is the optional CancelationToken used for canceling a TPL task, more on canceling our AsyncTask can be found in the next section. A good read on canceling and reporting can be found here

Besides the cancellation token, we have yet another important player in the picture, SynchronizationContext. One of the key responsibilities our AsyncTask has is to successively delegate calls from any separate working thread to the main GUI thread, that's where the syncContext fits in. I still keep the utility static method PerformInvoke(Control ctrl, Action action) just for reference, what you need otherwise to call the GUI thread from different threading context.

Error handling and reporting is a topic of great interest to me. So I will try not to dwell on the topic in-depth, but simply point you to the quite traditional way of handling exceptions using a try-catch expression. Long story short, I found it the only working solution for me. However, there exists an alternative as far as async tasks are concerned, see Exception Handling (Task Parallel Library) , using continuations. However, I was loosing stack trace on my exceptions, which is a must have in my book. Something worth investigating. Read more on the topic in the conclusion.

In case a CancelationException is thrown, which seems the recommended approach, I do handle it and propagate to the Err function delegate. This might not be exactly what you're looking for.

The TPL DemoForm

public partial class TestForm : Form
{
    private  CancellationTokenSource cancelationTokenSource;

    public TestForm()
    {
        InitializeComponent();

        this.progressBar1.Value = 0;
        this.progressBar1.Maximum = 0;

        this.cancelationTokenSource = new CancellationTokenSource();
    }

    private void OnGoClick(object sender, EventArgs e)
    {
        int count = int.Parse((string)(this.comboBox1.SelectedItem ?? "1"));

        this.progressBar1.Maximum += count;

        this.cancelationTokenSource = new CancellationTokenSource();

        int i;
        for (i = 1; i <= count; i++)
        {
            new AsyncTask<int, int, string>(cancelationTokenSource.Token)
            {
                Go = progress =>
                {
                    this.progressBar1.Value += 1;//usually = progress; 
                },
                Do = (input, progress) =>
                {
                    int timeout = RandomRoulette();

                    if (timeout % 2000 == 0)
                    {
                        throw new InvalidOperationException("Muuuuuu...");
                    }
                        
                    Thread.Sleep(timeout);

                    if (progress.IsCancellationRequested)
                    {
                        //clean up here ...
                        progress.ThrowIfCancel();
                    }

                    progress.Report(100);
                        
                    return string.Format("T{0} returned after {1} secs", input, timeout / 1000);
                },
                Post = (output) =>
                {
                    PrintInTheGUI(output);
                },
                Err = (exception) =>
                {
                    this.progressBar1.Value += 1;
                    PrintInTheGUI("Holly C0W! An Exception: " + exception.Message);
                    return false;
                }

            }.Execute(i);
        }

        //this will be the first thing you will see after pressing the execute button
        this.PrintInTheGUI("Threads spawned :" + (i - 1).ToString());
    }

    private static int RandomRoulette()
    {
        int timeout = new Random().Next(1, 13) * 1000;

        return timeout;
    }

    private void PrintInTheGUI(string text)
    {
        this.textBox1.Text = text + System.Environment.NewLine + this.textBox1.Text;
    }

    private void OnCancel(object sender, EventArgs e)
    {
        this.cancelationTokenSource.CancelAfter(666);
        PrintInTheGUI("Canceling......");
    }
}

Having looked at the usage example above it looks quite similar to before. Still there are some points I consider worth mentioning.

Improved Contract Interface

To start, the interface is extended with a new Err function delegate. Before, following the original Android SDK design, the error was reported as parameter to the Post function delegate. Now, in case an exception got thrown from your Do function it will get propagated to you Err function, where you will have a chance to report to the user.

Pay Attention to the fact that the Err returns a bool value whether to re-throw the exception or to consume it otherwise. Best practices dictates  you should let the exception propagate up the stack. 

TPL offers a suitable IProgress<T> interface 

Still we need a way to yield back for cancellation in addition to progress reporting. So we still need that callback reference to the base AsyncTask instance from within our new anonymous class. The class AsyncTaskCallBack serves the purpose. It is passed as second parameter to our Do function. It is named progress, you might consider coming with more appropriate name to serve its dual purpose of reporting and canceling. 

Random Exceptions

To demonstrate correct error handling some of the AsyncTasks will throw from their Do method. The random effect is dependent on the seconds to sleep time, whether it is even seconds. So every now and then you should get the Muu exception. You might consider switching VS debugger to not break automatically on exceptions.

The Cancelation token is external and shared 

The cancellation token is provided as external dependency in the constructor of AsyncTask. There are a few precautions as a result of it that you need to keep in mind. First, if you trigger the cancellation token, any future tasks you try to run will be in fault state on creation, hence never actually executed.  Second, due to the aforementioned cause and affect I need to recreate new instance of the token object every time I schedule a new batch, otherwise after canceling once you will never be able to run yet another batch.  

One last thing is: this.cancelationTokenSource.CancelAfter(666). 666 is nothing special, just to screw with religious people I use it quite often. The idea is to delay the actual call to the canceling mechanism only after the method yields back and we leave some time for the GUI to refresh. Consider, try it yourself, what happens when you invoke AsyncTask 1000 or more times and only after you try canceling.

Future Improvements 

Old 

One very intuitive way of improving is to cut our dependence on the GenericBackgroundWorker. Although it is a very good implementation, it has plenty of unnecessary overhead. For one, it relies on the same events pattern found in C# I wanted to avoid at the first place. Support for canceling the async task. It is already supported by the GenericBackgroundWorker so I will leave it to you to implement additional executor strategies, see for example (SERIAL_EXECUTOR, THREAD_EXECUTOR) of the original Android AsyncTask. The current implementation is already giving you plenty of space to do small logic on the GUI thread. For instance, limiting the maximum number of spawned AsynTask instances is as easily as well as correctly achievable by simply using some count variable. You can manipulate it safely from anywhere BUT inside your Do function. A good choice in this case will be the Pre function. Of course, you are welcomed to come with yet another breed of your custom AsyncTask where such behavior is implemented from inside.

So, using async tasks in the latest C#5.0 language, I've managed to shorten the implementation quite a bit. On top of that, I've introduced canceling already improving my previous implementation feature wise.

Have I used the continuation concept in TPL, I could have come with even more concise implementation of AsyncTask. Consider the following alternative:

this.task = Task.Factory.StartNew(() => Do(input, this),
	cancelToken,
	taskCreateOptions,
	TaskScheduler.Default)
	.ContinueWith<Tout>((t) => { return t.Result; }, cancelToken, 
	  TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext())
	.ContinueWith<Tout>((t) => { Err(t.Exception); return default(Tout); }, 
	  cancelToken, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
	.ContinueWith<Tout>((t) => { return t.Result; }, TaskContinuationOptions.OnlyOnRanToCompletion)
	.ContinueWith<Tout>((t) => { Err(t.Exception); return default(Tout); }, TaskContinuationOptions.OnlyOnFaulted);

The fact that our cancellation token is external and shared might not be desirable in case you plan to have multiple not related AsyncTasks running. One obvious solution is to keep the token internal, specific for each instance. However, this will require keeping the actual instance of every AsyncTask in a queue you will need to manage yourself. Something I consider outside the scope of this article.

Conclusions

After the latest TPL addition to the article, we have now two concrete implementation of the same Contract driven design solution to the commonly arising concern of how to decouple execution from GUI to the rest of your application. The original version, although more verbose, gives more classic C# solution and is easily deployable in any CLR 2.0 or later environment. On the other hand the new TPL AsyncTask implementation is very compact, but it inherently brings more complexity. While the new async model in C# seems very easy and straightforward at first glance I found out that things are getting more complicated and convoluted below the surface. IMHO the exception handling needs extra attention. Canceling an sync tasks leaves even more space to be desired. To start, a cancellation is not a logic/flow exception and should not be handled through exceptions to start with. That is not entirely true as ultimately it is the good old pulling concept employed, which brings me to my final remark. You cannot really stop/abort an async tasks. That's right and you can experience yourself the affect I'm referring to if you experiment with the test form. Even after pressing the cancel button, any task which is already executing will continue so tile the end of its Thread.Sleep function. This is not an issue particular to C#/CLR, but a rather general problem usually solved by pulling for cancellation request every so steps while running your long computation algorithm. However, the big difference is the abstraction of async tasks, which comes at the expense of control in this case. If I were using threads directly, I still have the option to prematurely aborting (kill) a thread. Arguably a good solution, but AFAIK works well as far as you don't mess some shared memory/state along the way. Oh.. and considering the last paragraph in the previous section, it truly makes me believe we need AsyncCancel, since the GUI will freeze/unresponsive when you try canceling many task simultaneously.

Final Remarks

Use it on your own risk. The code in this article is entirely experimental and not production proof. In case you end up using it in production I will appreciate dropping a line and reflect on your experience.

Best wishes!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

pip010
Software Developer (Senior)
Bulgaria Bulgaria
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinprofessionalEmre Ataseven27-May-14 19:57 
GeneralMy vote of 1 PinmemberXcalllibur7-Apr-13 3:39 
GeneralRe: My vote of 1 Pinmemberpip01016-Apr-13 5:54 
GeneralMy vote of 5 Pinmembersam.hill7-Mar-13 12:02 
SuggestionSystem.ComponentModel.BackgroundWorker PinmemberAlexandru Lungu27-Nov-12 6:23 
GeneralRe: System.ComponentModel.BackgroundWorker Pinmemberpip01029-Nov-12 3:02 
GeneralRe: System.ComponentModel.BackgroundWorker PinmemberLouis Gale7-Apr-13 3:34 
GeneralRe: System.ComponentModel.BackgroundWorker Pinmemberpip01016-Apr-13 5:37 
QuestionUse of Async & Await (Framework 4.5) PinmemberPyledriver21-Nov-12 10:55 
AnswerRe: Use of Async & Await (Framework 4.5) Pinmemberpip01021-Nov-12 21:31 
GeneralRe: Use of Async & Await (Framework 4.5) PinmemberPyledriver21-Nov-12 21:59 
GeneralRe: Use of Async & Await (Framework 4.5) Pinmemberpip01021-Nov-12 22:34 
GeneralRe: Use of Async & Await (Framework 4.5) PinmemberPyledriver22-Nov-12 21:47 
GeneralMy vote of 2 PinmemberJon Clare21-Nov-12 3:01 
GeneralRe: My vote of 2 Pinmemberpip01021-Nov-12 4:28 
QuestionWhy not TPL [modified] Pinmemberjohannesnestler21-Nov-12 2:37 
AnswerRe: Why not TPL Pinmemberpip01021-Nov-12 6:52 
Question[My vote of 2] Take a look at System.Threading.Tasks Pinmembercommerce@jant.de21-Nov-12 2:34 
AnswerRe: [My vote of 2] Take a look at System.Threading.Tasks Pinmemberpip01021-Nov-12 6:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 7 Mar 2013
Article Copyright 2012 by pip010
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid