0

This is my C# function

public async Task GetAttendance(IEnumerable<Zone> zones)
{
    try
    {
        foreach (var zone in zones)
        {
            var req = new AttendeeRequestTO(zone.StartTime, zone.EndTime, zone.ZoneId.ToString(), accessToken);

            //THROWING COMPILE TIME ERROR
            zone.AttendanceCount = Task.Factory.StartNew(() => _vClient.GetAttendeesCount(req));
        }
    }
    catch (Exception ex)
    {
    }
}

Error

Error CS0029 Cannot implicitly convert type 'System.Threading.Tasks.Task>' to 'int?'

I don't want to apply await for each Task as each Task is independent & I want to each Task to run on its own context without waiting for any other Task.

I mean
Task T1 - GO to API , get the value & set the count
Task T2 - GO to API , get the value & set the count
Task T3 - GO to API , get the value & set the count

T2 should not wait for T1 to complete, T3 should not wait for T2 to complete & so on.

How do I assign the value of each Task's output to zone.AttendanceCount?

Kirk Larkin
  • 84,915
  • 16
  • 214
  • 203
Kgn-web
  • 7,047
  • 24
  • 95
  • 161

2 Answers2

5

The way to do asynchronous concurrency is to call each asynchronous method without using await, and then use await Task.WhenAll.

Since you have a collection and need a task for each one, you can call each asynchronous method using LINQ:

var tasks = zones.Select(async zone =>
{
  var req = new AttendeeRequestTO(zone.StartTime, zone.EndTime, zone.ZoneId.ToString(), accessToken);
  zone.AttendanceCount = await Task.Run(() => _vClient.GetAttendeesCount(req));
}).ToList();
await Task.WhenAll(tasks);

Note that I also changed StartNew to Run.

It would be better, if possible, to use a GetAttendeesCountAsync method on your client (assuming it's making an I/O call). If that method is available, you can avoid unnecessary use of the thread pool:

var tasks = zones.Select(async zone =>
{
  var req = new AttendeeRequestTO(zone.StartTime, zone.EndTime, zone.ZoneId.ToString(), accessToken);
  zone.AttendanceCount = await _vClient.GetAttendeesCountAsync(req);
}).ToList();
await Task.WhenAll(tasks);
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I like this Linq-based solution. The only thing that lacks here is exception handling. – Zack ISSOIR Feb 20 '19 at 18:11
  • 2
    Great. I just found a blog of yours https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html – Kgn-web Feb 20 '19 at 18:15
  • 1
    @ZackISSOIR: Yes; the code as-is will handle exception(s) by only raising a single exception at the `await Task.WhenAll` line. If you prefer handling every exception individually, the easiest solution is to put a `try`/`catch` within the `Select` lambda. – Stephen Cleary Feb 20 '19 at 19:03
  • @StephenCleary can you please take a look at this question https://stackoverflow.com/questions/55231438/is-it-possible-to-fire-3000-parallel-asynchronous-http-requests-from-asp-net-cor – Zack ISSOIR Mar 18 '19 at 23:52
0

Task.Factory.StartNew returns a Task or a Task<T> so if you want to get the result returned by the async operation you should await it.

You just need to add await:

zone.AttendanceCount = await Task.Factory.StartNew(() => _vClient.GetAttendeesCount(req));

Note also that it is recommended to use Task.Run instead of Task.Factory.StartNew.

Kirk Larkin
  • 84,915
  • 16
  • 214
  • 203
Zack ISSOIR
  • 964
  • 11
  • 24
  • Can you explain a little bit more about the last sentence? "_it is recommended to use Task.Run instead of Task.Factory.StartNew._" – Diego Osornio Feb 20 '19 at 18:15
  • 1
    @Unamed see this SO question. https://stackoverflow.com/questions/38423472/what-is-the-difference-between-task-run-and-task-factory-startnew – Zack ISSOIR Feb 20 '19 at 18:17