﻿<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>Ayende @ Rahien</title><link>http://ayende.com</link><description>Ayende @ Rahien</description><copyright>Copyright (C) Ayende Rahien  2004 - 2021 (c) 2026</copyright><ttl>60</ttl><item><title>gandjustas commented on Beware of big Task Parallel Library Operations</title><description>Bunter, for caller there is no difference how asynchronous call executes. But for whole system creation of new threads is very expensive operation. Async IO helps handle massive coucurrency with small number of threads. 

Async IO allows you to create scalable systems.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment43</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment43</guid><pubDate>Sat, 14 Apr 2012 16:12:29 GMT</pubDate></item><item><title>Omer Mor commented on Beware of big Task Parallel Library Operations</title><description>Oren,
regarding Rx:
with the latest v2.0 beta version you can write your query like this:

    var result = await Observable

        .Range(0, 10 * 1000)

        .ObserveOn(TaskPoolScheduler.Default)

        .Select(i =&gt; Observable.FromAsync(() =&gt; fetchDataAsync(i)))

        .Concat()

        .Sum();

This query will run the async operation one at a time, in the right order.
For explanation - see this thread in the Rx forum (where I posted your question): http://social.msdn.microsoft.com/Forums/en-US/rx/thread/f7171f3b-523a-4df7-a134-d7bf633cdc55.

I generalized your case and wrote a simple SelectAsync operator:

    public IObservable&lt;R&gt; SelectAsync&lt;T,R&gt;(IObservable&lt;T&gt; source, Func&lt;T,Task&lt;R&gt;&gt; asyncSelector)

    {

        return source

            .Select(value =&gt; Observable.FromAsync(() =&gt; asyncSelector(value)))

            .Concat();

    }

This allow the following simple query:

    var result = await Observable

        .Range(0, 10 * 1000)

        .ObserveOn(TaskPoolScheduler.Default)

        .SelectAsync(i =&gt; fetchDataAsync(i))

        .Sum();
</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment42</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment42</guid><pubDate>Sat, 14 Apr 2012 11:14:21 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Bunter,
Let us say that I have only 2 threads in my system.
If I am holding one waiting for an async operation, that is going to cause a lot of waiting, and will holdup the entire thread.
If I am using async operations, I am NOT holding a thread while the OS is doing the actual IO</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment41</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment41</guid><pubDate>Sat, 14 Apr 2012 00:14:21 GMT</pubDate></item><item><title>Bunter commented on Beware of big Task Parallel Library Operations</title><description>I still fail to grasp the difference - once you've given out the ball (instructions to run a task with a loop and bunch of calls) to TPL, how is it different for your main thread how this async task executes?</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment40</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment40</guid><pubDate>Fri, 13 Apr 2012 13:29:46 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Bunter,
During the async operation, I can give up my own thread and do something else in it.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment39</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment39</guid><pubDate>Fri, 13 Apr 2012 04:11:29 GMT</pubDate></item><item><title>gandjustas commented on Beware of big Task Parallel Library Operations</title><description>Code to chaining (not nesting) tasks:

        private static Task&lt;int&gt; ProcessList(List&lt;int&gt; list, int pos, int acc = 0)
        {
            var resultTcs = new TaskCompletionSource&lt;int&gt;();

            if (pos &gt;= list.Count)
            {
                resultTcs.TrySetResult(acc);

            }
            else
            {
                Task.Factory.StartNew(() =&gt; list[pos] + acc).ContinueWith(task =&gt;
                    {
                        if (task.IsCanceled)
                        {
                            resultTcs.TrySetCanceled();
                        }
                        else if (task.IsFaulted)
                        {
                            resultTcs.TrySetException(task.Exception.InnerExceptions);
                        }
                        else
                        {
                            ProcessList(list, pos + 1, task.Result).ContinueWith(loop =&gt;
                                {

                                    if (loop.IsCanceled)
                                    {
                                        resultTcs.TrySetCanceled();
                                    }
                                    else if (loop.IsFaulted)
                                    {
                                        resultTcs.TrySetException(task.Exception.InnerExceptions);
                                    }
                                    else
                                    {
                                        resultTcs.TrySetResult(loop.Result);
                                    }
                                }, TaskContinuationOptions.ExecuteSynchronously);


                        }

                    }, TaskContinuationOptions.ExecuteSynchronously);
            }

            return resultTcs.Task;
        }</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment38</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment38</guid><pubDate>Fri, 13 Apr 2012 02:32:01 GMT</pubDate></item><item><title>gandjustas commented on Beware of big Task Parallel Library Operations</title><description>Stackoverflow in this case is not caused by task calls with , it caused by task completion.


        private async static Task&lt;int&gt; ProcessList(List&lt;int&gt; list, int pos, int acc = 0)
        {
            if (pos &gt;= list.Count)
            {
                return acc; //(1)
            }

            var result = await Task.Factory.StartNew(() =&gt; list[pos] + acc);

            return await ProcessList(list, pos + 1, result);
        }

Task returned by ProcessList don't complete until all inner task completes. Recursive calls creates nested task on heap, without stakoverflow.
When execution reaches line (1) innermost task completes, and it caused parent task to complete synchronous (adding about 5 calls in stack).

This "task unwind" caused StackOverflow..

Continuations should be chained, not nested. TPL is very bad designed in this way. TPL should be considered as low-level async library. For production code prefer RX.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment37</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment37</guid><pubDate>Fri, 13 Apr 2012 02:17:52 GMT</pubDate></item><item><title>josh commented on Beware of big Task Parallel Library Operations</title><description>So, how about this?

        private static Task&lt;int&gt; ProcessList(IEnumerable&lt;int&gt; list)
        {
            
            var task = Task.Factory.StartNew(
                () =&gt;
                    {
                        var acc = 0;
                        foreach (int item in list)
                            acc += int.Parse(GetStringAsync(item).Result);
                        return acc;
                    });
            return task;
        }

        private static Task&lt;string&gt; GetStringAsync(int item)
        {
            return Task.Factory.StartNew(() =&gt; item.ToString());
        }</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment36</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment36</guid><pubDate>Fri, 13 Apr 2012 01:08:27 GMT</pubDate></item><item><title>Bunter commented on Beware of big Task Parallel Library Operations</title><description>Since you need syncronization anyway (calling operations in given sequence), why bother with async task for every single operation when you could just have single async task doing a loop and blocking?</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment35</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment35</guid><pubDate>Fri, 13 Apr 2012 00:52:57 GMT</pubDate></item><item><title>gandjustas commented on Beware of big Task Parallel Library Operations</title><description>TaskCreationOptions.PreferFairness dosen't solve problem.

await Task.Factory.StartNew(method) almost always leads to "task inlining", when task completes on same thread with parent task. 

If you need real async call - use Task.Factory.FromAsync with APM or TaskCompletionSource with EAP/</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment28</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment28</guid><pubDate>Thu, 12 Apr 2012 23:46:39 GMT</pubDate></item><item><title>Patrick Huizinga commented on Beware of big Task Parallel Library Operations</title><description>Hmm, can it be that in order for you code to show properly you need to have 2 newlines and indent it all with 4 spaces?

Just like StackOverflow, could've guessed that...</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment34</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment34</guid><pubDate>Thu, 12 Apr 2012 21:51:05 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Patrick,
If you use ConitnueWith, that is going to cause the StackOverflow, without it, it works.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment33</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment33</guid><pubDate>Thu, 12 Apr 2012 21:46:47 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Patrick,
YES, that would do it. I never considered just starting up a new task, instead of using ContinueWith.
Very good point.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment32</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment32</guid><pubDate>Thu, 12 Apr 2012 21:46:12 GMT</pubDate></item><item><title>Patrick Huizinga commented on Beware of big Task Parallel Library Operations</title><description>@Ayende: _Karg, My problem is, I have no idea how to do the waiting for the task inside my own task without actually holding up the thread._

wouldn't a combination of TaskCompletionSource and Task.ContinueWith() do the trick?

so basically:

var source = new TaskCompletionSource
GetTask().ContinueWith(t =&gt; source.TrySetX(...))
return source.Task

inside the ContinueWith you can do whatever you want</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment31</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment31</guid><pubDate>Thu, 12 Apr 2012 21:45:13 GMT</pubDate></item><item><title>Patrick Huizinga commented on Beware of big Task Parallel Library Operations</title><description>would this do the trick? (not tested, except in my head)

class Program
{
  static void Main()
  {
    var list = Enumerable.Range(0, 10000).ToList();
    var task = new ProcessList(list).Task;
    Console.WriteLine(task.Result);
  }

  sealed class ProcessList
  {
    readonly List{int} list;
    readonly TaskCompletionSource{int} result = new TaskCompletionSource{int}();
    int index, acc;

    public ProcessList(List{int} list)
    {
      this.list = list;
      MoveNext();
    }

    public Task{int} Task { get { return result.Task; } }

    void MoveNext()
    {
      if (list.Count == index) result.TrySetResult(acc);
      else Task.Factory.StartNew(ProcessStep);
    }

    void ProcessStep()
    {
      try
      {
        acc += list[index];
        index++;
        MoveNext();
      }
      catch (Exception e)
      {
        result.TrySetException(e);
      }
    }
  }
}</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment30</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment30</guid><pubDate>Thu, 12 Apr 2012 21:36:59 GMT</pubDate></item><item><title>Sam commented on Beware of big Task Parallel Library Operations</title><description>As far as I can tell, it is possible for the first method to run without throwing StackOverFlowException most of the time.

Doesn't Task.StartNew have an optimization that will run the body immediately if it's small? This would cause the StackOverflowException.

If the body were larger, then it would be captured into a lambda and run on the Threadpool, which should make it not throw the exception.

If that is the case, adding TaskCreationOptions.PreferFairness should force it to always run on the Threadpool.

The TPL CTP had AttachToParent as the default option, which caused a similar problem.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment27</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment27</guid><pubDate>Thu, 12 Apr 2012 21:32:30 GMT</pubDate></item><item><title>Duarte commented on Beware of big Task Parallel Library Operations</title><description>If you're using ContinueWith with the default TaskContinuationOptions, then there really should be no recursion, as the continuation will execute on a different thread (or the current one, after it returns and the stack unwinds). At least for the "server" profile they solve the recursion issue by using stack probes (via the Win32 api).</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment26</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment26</guid><pubDate>Thu, 12 Apr 2012 20:34:04 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Bunter,
What I am trying to do is to create an async process in which I do a set of async operation in seqeunce.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment29</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment29</guid><pubDate>Thu, 12 Apr 2012 19:50:21 GMT</pubDate></item><item><title>Bunter commented on Beware of big Task Parallel Library Operations</title><description>Do I get it correctly: you are trying to implement a use case with task parallel library which inherently is not parallel. </description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment25</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment25</guid><pubDate>Thu, 12 Apr 2012 19:42:44 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>tobi,
This is the code using a Console Application, .NET Client Profile 4.0 with x86.
It is possible that you need to replace the math stuff with an actual blocking call, like a web request to reproduce that.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment24</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment24</guid><pubDate>Thu, 12 Apr 2012 16:55:58 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Omer,
The actual operation I want to do is to run an async task (WebRequest.GetStringAsync()).
I don't see how this would make it work, and I have to run this ONE at at time, in the same sequence.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment23</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment23</guid><pubDate>Thu, 12 Apr 2012 16:53:38 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Karg,
My problem is, I have no idea how to do the waiting for the task inside my own task without actually holding up the thread.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment22</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment22</guid><pubDate>Thu, 12 Apr 2012 16:52:28 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Aaron,
No, I can't do that.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment21</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment21</guid><pubDate>Thu, 12 Apr 2012 16:51:44 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Ryan,
Except that in my case, I actually need to execute an OPERATION in sequence . I.e: Making a lot of web requests</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment20</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment20</guid><pubDate>Thu, 12 Apr 2012 16:51:21 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Josh,
Assume that each of the operation is a separate async task. For example, an async web request</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment19</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment19</guid><pubDate>Thu, 12 Apr 2012 16:50:16 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>Josh,
Yes, when you are using an async method.</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment18</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment18</guid><pubDate>Thu, 12 Apr 2012 16:49:30 GMT</pubDate></item><item><title>Ayende Rahien commented on Beware of big Task Parallel Library Operations</title><description>gandjustas ,
that is awesome, but requires VS 2011 compiler</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment17</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment17</guid><pubDate>Thu, 12 Apr 2012 16:49:09 GMT</pubDate></item><item><title>tobi commented on Beware of big Task Parallel Library Operations</title><description>Ayende,

actually I'd like to repro this because I have such code in production. Under what circumstances will this cause a StackOverflow? I tried x86/x64 cross-product debug/(release without debugger) with 1000 * 1000. Works correctly.
</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment16</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment16</guid><pubDate>Thu, 12 Apr 2012 15:45:41 GMT</pubDate></item><item><title>tobi commented on Beware of big Task Parallel Library Operations</title><description>Ayende,

the first one does not throw for me. The TPL has stack depth checks.

Can't test the second one but I suspect it won't throw either. RunSynchronously gets ignored if the stack is too deep.
</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment15</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment15</guid><pubDate>Thu, 12 Apr 2012 15:32:21 GMT</pubDate></item><item><title>Omer Mor commented on Beware of big Task Parallel Library Operations</title><description>Here's another attempt at formatting the Rx query:

var result = await Enumerable

  .Range(0, 10 * 1000)
  
  .ToObservable()
  
  .ObserveOn(Scheduler.TaskPool)
  
  .Select(i =&gt; fetchDataFromRemoteServerFor(i))
  
  .Sum(); // or use more complex aggregations with .Aggregate(...)</description><link>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment14</link><guid>http://ayende.com/156065/beware-of-big-task-parallel-library-operations#comment14</guid><pubDate>Thu, 12 Apr 2012 15:31:30 GMT</pubDate></item></channel></rss>