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?
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.
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.
Related
What is the best way to add non-Task element to existing Task<List<MyClass>>?
I have to add MyClass element nonTaskClass into result x that is Task List. The thing is that it is not in any way async. It is just some pure conditional logic that results into object. Something like this:
public Task<List<MyClass>> CheckAsync(Input input) {
// Get some class based on sync logic/conditional logic
var nonTaskClass = new MyClass();
if (input.Form == 1 || input.Country = "Lithuania") {
nonTaskClass.Message = "Some message goes here";
} else {
nonTaskClass.Message = "Another message";
}
// Create tasks (calls different methods/services and get data)
// Task list actually is based on "nonTaskClass"
// Get data from tasks and return
var x = Task.WhenAll(tasks).ContinueWith(t =>
{
return t.Result.Select(o => new MyClass
{
Message = o.Message
}).ToList();
});
// Need to add nonTaskClass into x, how?!!!!!
return x;
}
If it wasn't feasible to refactor the method - including making it async - I would add Task.FromResult(nonTaskClass) to the list of tasks:
tasks.Add(Task.FromResult(nonTaskClass));
// Get data from tasks and return
var x = Task.WhenAll(tasks)...
I would make it async and change it to:
public async Task<List<MyClass>> CheckAsync(Input input)
{
var nonTaskClass = new MyClass();
if (input.Form == 1 || input.Country = "Lithuania")
{
nonTaskClass.Message = "Some message goes here";
}
else
{
nonTaskClass.Message = "Another message";
}
var tasks = GetSomeTasks();
return (await Task.WhenAll(tasks))
.Select(x => new MyClass { Message = x.Message })
.Append(nonTaskClass)
.ToList();
}
I have a loop creating three tasks:
List<Task> tasks = new List<Task>();
foreach (DSDevice device in validdevices)
{
var task = Task.Run(() =>
{
var conf = PrepareModasConfig(device, alternativconfig));
//CHECK-Point1
string config = ModasDicToConfig(conf);
//CHECK-Point2
if (config != null)
{
//Do Stuff
}
else
{
//Do other Stuff
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
it calls this method, where some data of a dictionary of a default-config gets overwritten:
private Dictionary<string, Dictionary<string, string>> PrepareModasConfig(DSDevice device, string alternativeconfig)
{
try
{
Dictionary<string, Dictionary<string, string>> config = new Dictionary<string, Dictionary<string, string>>(Project.project.ModasConfig.Config);
if (config.ContainsKey("[Main]"))
{
if (config["[Main]"].ContainsKey("DevName"))
{
config["[Main]"]["DevName"] = device.ID;
}
}
return config;
}
catch
{
return null;
}
}
and after that, it gets converted into a string with this method:
private string ModasDicToConfig(Dictionary<string, Dictionary<string, string>> dic)
{
string s = string.Empty;
try
{
foreach (string key in dic.Keys)
{
s = s + key + "\n";
foreach (string k in dic[key].Keys)
{
s = s + k + "=" + dic[key][k] + "\n";
}
s = s + "\n";
}
return s;
}
catch
{
return null;
}
}
But every Tasks gets the exact same string back.
On //CHECK-Point1 I check the Dic for the changed value: Correct Value for each Task
On //CHECK-Point2 I check the String: Same String on all 3 Tasks (Should be of course different)
Default-Dictionary looks like this: (shortened)
{
{"[Main]",
{"DevName", "Default"},
...
},
...
}
The resulting string look like that:
[Main]
DevName=003 <--This should be different (from Device.ID)
...
[...]
EDIT:
I moved the methods to execute outside the Task. Now I get the correct Results. So I guess it has something to do with the Task?
List<Task> tasks = new List<Task>();
foreach (DSDevice device in validdevices)
{
var conf = PrepareModasConfig(device, alternativconfig));
//CHECK-Point1
string config = ModasDicToConfig(conf);
//CHECK-Point2
var task = Task.Run(() =>
{
if (config != null)
{
//Do Stuff
}
else
{
//Do other Stuff
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
The problem isn't caused by tasks. The lambda passed to Task.Run captures the loop variable device so when the tasks are executed, all will use the contents of that variable. The same problem would occur even without tasks as this SO question shows. The following code would print 10 times:
List<Action> actions = new List<Action>();
for (int i = 0; i < 10; ++i )
actions.Add(()=>Console.WriteLine(i));
foreach (Action a in actions)
a();
------
10
10
10
10
10
10
10
10
10
10
If the question's code used an Action without Task.Run it would still result in bad results.
One way to fix this is to copy the loop variable into a local variable and use only that in the lambda :
for (int i = 0; i < 10; ++i )
{
var ii=i;
actions.Add(()=>Console.WriteLine(ii));
}
The question's code can be fixed by copying the device loop variable into the loop:
foreach (DSDevice dev in validdevices)
{
var device=dev;
var task = Task.Run(() =>
{
var conf = PrepareModasConfig(device, alternativconfig));
Another way is to use Parallel.ForEach to process all items in parallel, using all available cores, without creating tasks explicitly:
Parallel.ForEach(validdevices,device=>{
var conf = PrepareModasConfig(device, alternativconfig));
string config = ModasDicToConfig(conf);
...
});
Parallel.ForEach allows limiting the number of worker tasks through the MaxDegreeOfParallelism option. It's a blocking call because it uses the current thread to process data along with any worker tasks.
I have a function
public Task<IEnumerable<T>> GetVersionsAsync<T>(string key)
{
var request = GetVersionsRequestBuilder.Create(_databaseName, _collectionName).WithKey(key);
return Task.Run(() =>
{
var records = _myReader.GetAllVersionsForKey(request);
var returnValue = records.Select(record =>
{
var document = _mySerializer.Deserialize<T>(record.document);
return document;
});
return returnValue;
});
}
This is skipping first value and giving all rest results
However, if I change the LINQ part to ForEach, then it gives all records
var returnValue = new List<T>();
foreach (var record in records)
{
var document = _mySerializer.Deserialize<T>(record.document);
returnValue.Add(document);
}
return returnValue as IEnumerable<T>;
Is this due to LINQ's Select projection being inside Task.Run?
Do not mix IEnumerables and Tasks. Deferred Execution and parallel execution at the same time are really not playing well together.
I don't know what specifically trigger your weird behavior, but generally speaking, your task is useless, because it does not do what you think it does. It does not deserialize all elements. The task is done almost immediately, and it returns the state-machine that is your IEnumerable. Only when you materialize it, after your task is done, it will actually do what you intend to do with the LinQ.
So, do not mix it. If you want it to run async, do so, but materialize it:
public Task<List<T>> GetVersionsAsync<T>(string key)
{
var request = GetVersionsRequestBuilder.Create(_databaseName, _collectionName).WithKey(key);
return Task.Run(() =>
{
var records = _myReader.GetAllVersionsForKey(request);
var returnValue = records.Select(
record => _mySerializer.Deserialize<T>(record.document)
).ToList(); // materialize it
return returnValue;
});
}
What you propbably should do is make the method async and call the async version of your database reader instead of faking it.
I don't disagree with Nvoigt's advice but I ran two dummy versions of the code. The first version still uses IEnumerable and the second materializes into the List type. I got the same result with each which is that all the results were returned as expected. I have to conclude then that the issue is inside of the Deserialize call.
V1:
static async Task Main()
{
var insideTask = await GetFruitsAsync();
foreach (var obj in insideTask)
{
Console.WriteLine("{0}", obj);
}
return;
}
public static Task<IEnumerable<string>> GetFruitsAsync()
{
IEnumerable<string> fruits = new string[] { "apple", "banana", "mango", "orange",
"passionfruit", "grape" };
return Task.Run(() =>
{
var query = fruits.Select(fruit =>
{
var returnValue = fruit.Substring(0, 2);
return returnValue;
});
return query;
});
}
V2:
static async Task Main()
{
var insideTask = await GetFruitsAsync();
foreach (var obj in insideTask)
{
Console.WriteLine("{0}", obj);
}
return;
}
public static Task<List<string>> GetFruitsAsync()
{
List<string> fruits = new List<string> { "apple", "banana", "mango", "orange",
"passionfruit", "grape" };
return Task.Run(() =>
{
var query = fruits.Select(fruit =>
{
var returnValue = fruit.Substring(0, 2);
return returnValue;
});
return query.ToList();
});
}
I am trying to unit test a method that sends some notifications using FCM.This method takes a list of batches and for each batch it creates a Task of IBatchResponse where IBatchResponse is an interface I have made so I can mock the return type of the actual service sending the notification. Then using Task.WhenAny() I am proccessing the tasks as they complete.
public static async Task<NotificationRes> SendPushNotifications(List<Batch> batches, INotificationService notificationService)
{
var notificationTasks = new List<Task<IBatchResponse>>();
try
{
foreach (var batch in batches)
{
notificationTasks.Add(notificationService.SendNotification(guardTokens, "android"));
}
while (notificationTasks.Count > 0)
{
var finishedTask = await Task.WhenAny(notificationTasks);
var taskIndex = notificationTasks.FindIndex(task => task == finishedTask);
notificationTasks.Remove(finishedTask);
var finishedTaskResult = await finishedTask;
for (int i = 0; i < finishedTaskResult.Responses.Count; i++)
{
....
}
}
catch (Exception)
{
throw;
}
}
And here is the code for the test I am trying to make.I have mocked the wrapper service that sends the notification and the return type of notificationService.SendNotification (IBatchResponse).
[Test]
public async Task SendPushNotifications_test()
{
...
var batchSendResponses = new List<ISendResponse>();
....
var mockNotifService = new Mock<INotificationService>();
var mockBatchRes = new Mock<IBatchResponse>();
mockBatchRes.Setup(res => res.Responses).Returns(batchSendResponses);
var task = Task.FromResult(mockBatchRes.Object);
mockNotifService.Setup(ns => ns.SendNotification(new string[5] { "token1", "token2", "", "", "" }, "android")).Returns(task);
var notRes = await Utils.SendPushNotifications(batches, mockNotifService.Object);
}
The problem is that if 2 batches exist the finishedTask Result is always null but if I have 1 batch it works.What am I missing here.
I have found the problem which is in the mockNotifService.Setup() method.
mockNotifService.Setup(ns => ns.SendNotification(new string[5] { "token1", "token2", "", "", "" }, "android")).Returns(task);
Currently in ns.SendNotification() I am hardcoding the params and this seems to affect the Task returned or it doesn't produce the task I specify in .Returns() and produces an empty one without the Result. So I changed this to:
mockNotifService.Setup(ns => ns.SendNotification(It.IsAny<string []>(), It.IsAny<string>())).Returns(Task.FromResult(mockBatchRes.Object));
and the test passed.
The below method doesn't compile. Alternatives?
public static async Task<IEnumerable<object[]>> GetRecordsAsync(
this Transaction transaction,
string commandText,
params SqlParameter[] parameters)
{
// Get a SqlDataReader
var reader = await transaction.GetReaderAsync(commandText, parameters);
var fieldCount = -1;
// Begin iterating through records asynchronously
while (await reader.ReadAsync()) // Note we don't loop until .ReadAsync returns a boolean
{
// Grab all the field values out
if (fieldCount < 0)
fieldCount = reader.FieldCount;
var fields = new object[fieldCount];
reader.GetValues(fields);
// Yield return the field values from this record
yield return fields;
}
}
Error message:
The body of 'TransactionExtensions.GetRecordsAsync(Transaction, string, params SqlParameter[])' cannot be an iterator block because 'Task>' is not an iterator interface type
I don't see a way to adapt this answer to a similar sounding (but different) question, because I don't know a priori how many times the loop will go.
Edit: fixed formatting
Based on #SLaks's comment to the question, here's a general alternative using Reactive Extensions:
/// <summary>
/// Turns the given asynchronous functions into an IObservable
/// </summary>
static IObservable<T> ToObservable<T>(
Func<Task<bool>> shouldLoopAsync,
Func<Task<T>> getAsync)
{
return Observable.Create<T>(
observer => Task.Run(async () =>
{
while (await shouldLoopAsync())
{
var value = await getAsync();
observer.OnNext(value);
}
observer.OnCompleted();
}
)
);
}
Example usage, tailored to solve the question's specific case:
/// <summary>
/// Asynchronously processes each record of the given reader using the given handler
/// </summary>
static async Task ProcessResultsAsync(this SqlDataReader reader, Action<object[]> fieldsHandler)
{
// Set up async functions for the reader
var shouldLoopAsync = (Func<Task<bool>>)reader.ReadAsync;
var getAsync = new Func<SqlDataReader, Func<Task<object[]>>>(_reader =>
{
var fieldCount = -1;
return () => Task.Run(() =>
{
Interlocked.CompareExchange(ref fieldCount, _reader.FieldCount, -1);
var fields = new object[fieldCount];
_reader.GetValues(fields);
return fields;
});
})(reader);
// Turn the async functions into an IObservable
var observable = ToObservable(shouldLoopAsync, getAsync);
// Process the fields as they become available
var finished = new ManualResetEventSlim(); // This will be our signal for when the observable completes
using (observable.Subscribe(
onNext: fieldsHandler, // Invoke the handler for each set of fields
onCompleted: finished.Set // Set the gate when the observable completes
)) // Don't forget best practice of disposing IDisposables
// Asynchronously wait for the gate to be set
await Task.Run((Action)finished.Wait);
}
(Note that getAsync could be simplified in the above code block, but I like how explicit it is about the closure that's being created)
...and finally:
// Get a SqlDataReader
var reader = await transaction.GetReaderAsync(commandText, parameters);
// Do something with the records
await reader.ProcessResultsAsync(fields => { /* Code here to process each record */ });
Don't return a Task<IEnumerable<T>> and don't even use Task at all for this; instead, return an IAsyncEnumerable<T>. No need for third-party libraries or other workarounds, no need to even alter the body of your original method.
public static async IAsyncEnumerable<object[]> GetRecordsAsync(
this Transaction transaction,
string commandText,
params SqlParameter[] parameters)
{
// Get a SqlDataReader
var reader = await transaction.GetReaderAsync(commandText, parameters);
var fieldCount = -1;
// Begin iterating through records asynchronously
while (await reader.ReadAsync()) // Note we don't loop until .ReadAsync returns a boolean
{
// Grab all the field values out
if (fieldCount < 0)
fieldCount = reader.FieldCount;
var fields = new object[fieldCount];
reader.GetValues(fields);
// Yield return the field values from this record
yield return fields;
}
}
I solved it without third-party extensions:
public async Task<IEnumerable<Item>> GetAllFromDb()
{
OracleConnection connection = null;
DbDataReader reader = null;
try
{
connection = new OracleConnection(connectionString);
var command = new OracleCommand(queryString, connection);
connection.Open();
reader = await command.ExecuteReaderAsync();
return this.BuildEnumerable(connection, reader);
}
catch (Exception)
{
reader?.Dispose();
connection?.Dispose();
throw;
}
}
private IEnumerable<Item> BuildEnumerable(OracleConnection connection, DbDataReader reader)
{
using (connection)
using (reader)
{
while (reader.Read())
{
var item = new Item()
{
Prop = reader.GetString(0),
};
yield return item;
}
}
}
This example is for Oracle Data Reader but the same approach is applicable to any asynchronous operation combined with yield return