4

I have some async method

 public static Task<JObject> GetUser(NameValueCollection parameters) { return CallMethodApi("users.get", parameters, CallType.HTTPS); } 

And I write method below

public static IEnumerable<JObject> GetUsers(IEnumerable<string> usersUids, Field fields) { foreach(string uid in usersUids) { var parameters = new NameValueCollection { {"uids", uid}, {"fields", FieldsUtils.ConvertFieldsToString(fields)} }; yield return GetUser(parameters).Result; } } 

This method is asynchronous? How to write this using Parallel.ForEach?

2 Answers 2

5

Something kind of like this.

public static IEnumerable<JObject> GetUsers(IEnumerable<string> usersUids, Field fields) { var results = new List<JObject> Parallel.ForEach(usersUids, uid => { var parameters = new NameValueCollection { {"uids", uid}, {"fields", FieldsUtils.ConvertFieldsToString(fields)} }; var user = GetUser(parameters).Result; lock(results) results.Add(user); }); return results; } 

NOTE: The results won't be in the same order as you expect.

Sign up to request clarification or add additional context in comments.

2 Comments

Note that there is a race condition in the above code, as results list is being modified concurrently. Unpredictable results and failures will randomly occur. At least you would need a lock around the Add operation.
Thanks bojan... I just typed the code in with no testing to use as an illustration... it appears it just got used verbatim. Ouch! I fixed the race condition anyway
2

Your method is not asynchronous. Assuming your GetUser method already starts an asynchronous task, Parallel.ForEach would use additional threads just to start off your tasks, which is probably not what you want.

Instead, what you probably want to do is to start all of the tasks and wait for them to finish:

public static IEnumerable<JObject> GetUsers(IEnumerable<string> usersUids, Field fields) { var tasks = usersUids.Select( uid => { var parameters = new NameValueCollection { {"uids", uid}, {"fields", FieldsUtils.ConvertFieldsToString(fields)} }; return GetUser(parameters); } ).ToArray(); Task.WaitAll(tasks); var result = new JObject[tasks.Length]; for (var i = 0; i < tasks.Length; ++i) result[i] = tasks[i].Result; return result; } 

If you also want to start them in parallel you can use PLINQ:

 var tasks = usersUids.AsParallel().AsOrdered().Select( uid => { var parameters = new NameValueCollection { {"uids", uid}, {"fields", FieldsUtils.ConvertFieldsToString(fields)} }; return GetUser(parameters); } ).ToArray(); 

Both code snippets preserve relative ordering of uids and returned objects - result[0] corresponds to usersUids[0], etc.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.