I have a method that returns a object. In my parent function I have a list of IDs.
I would like to call the method for each ID I have and then have the objects added to a list. Right now I have written a loop that calls the method passing each ID and waits for the returned object and then goes to the next ID.
Can this be done in parallel? Any help here would be most helpful.
Something like this maybe:
List<int> ids = new List<int>();
List<object> result = new List<object>();
Parallel.ForEach(ids, (id, state, index) => {
result.Add(new { Id = id }); // You class instance here.
});
I think Task parallel libraries will help you
Task[] tasks = new Task[2];
tasks[0] = Task.Factory.StartNew(() => YourFunction());
tasks[1] = Task.Factory.StartNew(() => YourFunction());
Task.WaitAll(tasks);// here it will wait untill all the functions get completed
Related
I'm using the UniRx flavor of Reactive Extensions for the Unity3D game engine.
Unity uses C#, so I guess it's similar to Rx.NET.
I need a more beautiful way of checking when several observable sequences complete.
In the example below, one of the sequences is dependent on the outcome of the first (since it needs an integer for processID).
The observables are both of type IObservable<string>.
var processListObservable = APIBuilder
.GetProcessList(authInfo.Token, authInfo.PlatformURL, (int)product.Id)
.Subscribe(listJson =>
{
processList = ProcessList.FromJson(listJson);
int processID = (int)processList.Processes[0].ProcessDataId;
//Retrieve Detailed information of the first entry
var processDetailsObservable = APIBuilder
.GetProcessDetails(token, platformURL, product.Id, processID)
.Subscribe(detailsJson =>
{
processData = ProcessData.FromJson(detailsJson);
SetupPlotView();
});
});
Any hint would be highly appreciated. Also some suggestions to solve the same scenario minus the dependency on the result of the first sequence.
Instead of putting your code into the Subscribe handler, you could make it part of the sequence. You could use the Select operator in order to project each listJson to an IObservable<string> (resulting to a nested IObservable<IObservable<string>>), and then flatten the sequence by using either the Concat or the Merge operator, depending on whether you want to prevent or allow concurrency.
var processListObservable = APIBuilder
.GetProcessList(authInfo.Token, authInfo.PlatformURL, (int)product.Id)
.Select(listJson =>
{
var processList = ProcessList.FromJson(listJson);
int processID = (int)processList.Processes[0].ProcessDataId;
return APIBuilder.GetProcessDetails(token, platformURL, product.Id, processID);
})
.Concat() // or .Merge() to allow concurrency
.ObserveOn(SynchronizationContext.Current) // Optional
.Do(detailsJson =>
{
var processData = ProcessData.FromJson(detailsJson);
SetupPlotView(processData);
});
await processListObservable.DefaultIfEmpty(); // Start and await the operation
The await in the final line will cause an implicit subscription to the processListObservable, and your code will execute as a side-effect of this subscription.
I have a list of headlines that I am trying to process by using a Task that updates a parameter of the headline object. The code I am trying to do it with does not actually populate the parameter properly. When I debug it, I can see that the setters are being activated and properly updating the backing fields, but when examined after Task.WhenAll , none of the properties are in fact set to their expected values.
//**Relevant signatures are:**
class Headline{
public Uri Uri { get; set; }
public IEnumerable<string> AttachedEmails { get; set; } = Enumerable.Empty<string>();
}
async Task<IEnumerable<string>> GetEmailsFromHeadline(Uri headlineUri) {
//bunch of async fetching logic that populates emailNodes correctly
return emailNodes.Select(e => e.InnerText).ToList();
}
//**Problem Area**
//Initiate tasks that start fetching information
var taskList =
postData
.Select(e => new HttpRequest() { Uri = actionUri, PostData = e })
.Select(e => Task.Run(() => GetHeadlines(e)))
.ToList();
//Wait till complete
await Task.WhenAll(taskList);
//Flatten list
var allHeadlines =
taskList
.SelectMany(e => e.Result.ToList());
//After this section of code I expect every member AttachedEmails property to be properly updated but this is not what is happening.
var headlineProcessTaskList =
allHeadlines
.Select(e => Task.Run( new Func<Task>( async () => e.AttachedEmails = await GetEmailsFromHeadline(e.Uri) ) ) )
.ToList();
await Task.WhenAll(headlineProcessTaskList);
There isn't enough code to reproduce the problem. However, we can guess that:
allHeadlines is IEnumerable<Headline>
Since this is IEnumerable<T>, it is possible that it is a LINQ query that has been deferred. Thus, when you enumerate it once in the ToList call, it creates Headline instances that have their AttachedEmails set. But when you enumerate it again later, it creates new Headline instances.
If this is correct, then one solution would be to change the type of allHeadlines to List<Headline>.
A similar problem could occur in GetEmailsFromHeadline, which presumably returns Task<IEnumerable<string>> and not IEnumerable<string> as stated. If the enumerable is deferred, then the only asynchronous part is defining the query; executing it would be after the fact - more specifically, outside the Task.Run. You might want to consider using ToList there, too.
On a side note, the new Func is just noise, and the wrapping of an asynchronous task in Task.Run is quite unusual. This would be more idiomatic:
var headlineProcessTaskList =
allHeadlines
.Select(async e => e.AttachedEmails = await GetEmailsFromHeadline(e.Uri) )
.ToList();
or, if the Task.Run is truly necessary:
var headlineProcessTaskList =
allHeadlines
.Select(e => Task.Run( () => e.AttachedEmails = (await GetEmailsFromHeadline(e.Uri)).ToList() ) )
.ToList();
documents is a IDictionary<string, string> where the parameters are <filename, fileUrl>
DocumentHandler.Download() returns a Task<Memorystram>
This code works:
foreach (var x in documents.Keys)
{
var result = await DocumentHandler.Download(new Uri(documents[x]));
// other code
}
however it rund synchronously.
In order to run it all async i wrote this code:
var keys =
documents.Keys.Select(async x =>
{
return Tuple.Create(x, await DocumentHandler.Download(new Uri(documents[x])));
});
await Task.WhenAll(keys);
foreach (var item in keys)
{
var tpl = item.Result;
// other code
}
It doesn't work, it crashes without showing an exception on the last line var tpl = item.Result; Why?
Your keys variable will create a new set of tasks every time you evaluate it... so after waiting for the first set of tasks to complete, you're iterating over a new set of unfinished tasks. The simple fix for this is to add a call to ToList():
var keys = documents
.Keys
.Select(async x => Tuple.Create(x, await DocumentHandler.Download(new Uri(documents[x]))))
.ToList();
await Task.WhenAll(keys);
foreach (var item in keys)
{
var tpl = item.Result;
// other code
}
I want to combine the result of 2 tasks in one List collection.
Make sure that- I want to run both methods in parallel.
Code:
List<Employee> totalEmployees = new List<Employee>();
Method1:
public async Task<IEnumerable<Employee>> SearchEmployeeFromDb();
Method2:
public async Task<IEnumerable<Employee>> GetEmployeeFromService();
Now, I want to hold the result of these two methods in totalEmployees field, also these 2 method should run asynchronously.
While many answers are close, the cleanest and most efficient option is using Task.WhenAll combined with SelectMany:
async Task<IEnumerable<Employee>> Combine()
{
var results = await Task.WhenAll(SearchEmployeeFromDb(), GetEmployeeFromService());
return results.SelectMany(result => result);
}
This assumes that by parallel you mean concurrently. If you wish to run these operations with multiple threads from the beginning (including the synchronous parts of the async method) you need to also use Task.Run to offload work to a ThreadPool thread:
private async Task<IEnumerable<Employee>> Combine()
{
var results =
await Task.WhenAll(Task.Run(() => SearchEmployeeFromDb()), Task.Run(() => GetEmployeeFromService()));
return results.SelectMany(result => result);
}
Start both tasks
Use Task.WhenAll to wait for both tasks to finish
Use Enumerable.Concat to combine the results
var searchEmployeesTask = SearchEmployeeFromDb();
var getEmployeesTask = GetEmployeeFromService();
await Task.WhenAll(searchEmployeesTask, getEmployeesTask);
var totalEmployees = searchEmployeesTask.Result.Concat(getEmployeesTask.Result);
You can use Task.WhenAll to create a task which will return when all supplied tasks are complete
var result = await Task.WhenAll(SearchEmployeeFromDb(),GetEmployeeFromService());
var combined = result[0].Concat(result[1]);
Something like this should work:
var t1 = SearchEmployeeFromDb()
var t2 = GetEmployeeFromService()
await Task.WhenAll(t1, t2)
// Now use t1.Result and t2.Result to get `totalEmployees`
Use ConfigureAwait(false) to avoid deadlocking, define the tasks, execute and then await.
var fromDbTask = SearchEmployeeFromDb().ConfigureAwait(false);
var fromServiceTask = GetEmployeeFromService().ConfigureAwait(false);
var fromDbResult = await fromDbTask;
var totalEmployees = new List(fromDbResult);
var fromServiceResult = await fromServiceResult;
totalEmployees.AddRange(fromServiceResult);
... or use whichever way you want to merge the two lists.
I updated the solution, it was unneccessary to create the list and then append the first result. We wait for the first method to finish and then create the list.
Assuming I have all of the classes implementing IGenerator
List<IGenerator> generators = new List<IGenerator> { new Jane1Generator(), new Jane2Generator(), new JohnGenerator() };
And
public interface IGenerator
{
string GetFirstName();
Task<List<Items>> Generate();
}
So grouping generators by GetFirstName() will put Jane1 and Jane2 in the same category. How can I combine the Tasks for Jane1 and Jane2 into a single Task, and keep John separate. I want to combine the results of both Janes into a single List.
foreach (var groupedByName in generators.GroupBy(i => i.GetFirstName()))
{
//Combine Tasks of Jane1 & Jane2
//give me a new Task that's is the sum of Jane1 & Jane2 tasks.
//almost like List<Items>.Join(AnotherList) wrapped with a Task, so I can wait for both Tasks and Get combined Results instead.
}
so If I create a List<Task<List<Items>>> tasks = new List<Task<List<Items>>>();
I want tasks list to contain only two elements. one would be the combine tasks of Jane1&Jane2 and the other just John, so I can do
await Task.WhenAll(tasks);
Console.Write("Done");
You can start with GroupBy to group all of the generators by name.
Then select out those groups into the needed information, namely the name and the items.
To get a task representing all of the items for that group you can use Select to get a sequence of all of the items for each generator in the group. Giving that to WhenAll gives us a task that will be done when all of the generators finish. We can then add a continuation to that task that combines all of the items together into a single list.
var groups = generators.GroupBy(gen => gen.GetFirstName())
.Select(group => new
{
Name = group.Key,
Items = Task.WhenAll(group.Select(gen => gen.Generate()))
.ContinueWith(t => t.Result.SelectMany(items => items).ToList())
});
tasks.Add(Task<IEnumerable<Items>>.Factory.ContinueWhenAll(results.ToArray(),
myTasks =>
{
var newList = new List<Items>();
foreach (var i in results)
{
newList.AddRange(i.Result);
}
return DoSomething(newList.AsEnumerable());
}));
where results is the list of my groups items by their firstName.