I have a while loop in which I create and start tasks, as follows:
while (!stopped)
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10; i++)
tasks.add(Task.Factory.StartNew(() => DoSomething(i)));
Task.WaitAll(tasks.ToArray());
}
Would I get better performance if the tasks are created once before the while loop, and restarted everytime (because the data being passed to the function never changes)?
You can't restart task
http://msdn.microsoft.com/en-us/library/dd270682.aspx
By the way
Task.Factory.StartNew(() => {....})
is fast than
Task task = new Task(() => {...});
task.Start();
because no locking on Start method.
In your case use async io to get performance boost.
There is nothing fundamentally wrong with your code. This is a perfectly acceptable approach. You do not need to worry about the performance or expensive of creating tasks because the TPL was specifically designed to be used exactly like you have done.
However, there is one major problem with your code. You are closing over the loop variable. Remember, closures capture the variable and not the value. The way your code is written the DoSomething method will not be using the value of i that you think it should. Your code needs to be rewritten like this.
while (!stopped)
{
List tasks = new List();
for (int i = 0; i < 10; i++)
{
int capture = i;
tasks.add(Task.Factory.StartNew(() => DoSomething(capture)));
}
Task.WaitAll(tasks.ToArray());
}
As a side note you could use the Parallel.For method as an alternative. It is definitely a lot more compact of a solution if nothing else.
while (!stopped)
{
Parallel.For(0, 10, i => DoSomething(i));
}
Related
We had something like below
List<string> uncheckItems = new List<string>();
for (int i = 0; i < 100; i++)
{
uncheckItems.Add($"item {i + 1}");
}
var tasks = uncheckItems.Select(item =>
new Task(async () => await ProcessItem(item))
);
// Do some preparations
foreach (var task in tasks)
{
task.Start();
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("=====================================================All finished");
It seems to make sense but the program never able to reach the all finished line.
And if I adjust the workflow to run tasks immediately like remove the task.Start() loop and change to
var tasks = uncheckItems.Select(async item =>
await ProcessItem(item)
);
Then it works.
However, I wonder
Why it stucks?
Is there any way we can keep the workflow(create tasks without trigger them directly and start them later on) and still able to utilize WaitAll()?
The reason is the lazy enumeration evaluation, you are starting different tasks than waiting with Task.WaitAll. This can be fixed for example with next:
var tasks = uncheckItems.Select(item =>
new Task(async () => await ProcessItem(item))
)
.ToArray();
Though it will not achieve your goal (as I understand) of waiting all ProcessItem to finish. You can do something like new Task(() => ProcessItem(item).GetAwaiter().GetResult()) but I think it would be better to change your approach, for example make ProcessItem return a "cold" task or using your second snippet and moving tasks creation to the point where they needed to be started.
You should be next to the world expert in Task to be using the constructor. The documentation warns against that:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.
Rather than calling this constructor, the most common way to instantiate a Task object and launch a task is by calling the static Task.Run(Action) or TaskFactory.StartNew(Action) method.
If a task with no action is needed just for the consumer of an API to have something to await, a TaskCompletionSource should be used.
The Task constructor produces a non-started Task that will only start when Task.Start() is invoked, as you discovered.
The Task constructor also receives an Action (or Action<T>), so the return value is ignored. That means that, after started, the task will end as soon as async () => await ProcessItem(item) yields.
What you need is:
await Task.WhenAll(Enumerable.Range(0, 100).Select(i => ProcessItem($"item {i + 1}"));
Or, if you really have to block:
Task
.WhenAll(Enumerable.Range(0, 100).Select(i => ProcessItem($"item {i + 1}"))
.GetAwaiter().GetResult();
Get the select out of there.
List<string> uncheckItems = new List<string>();
for (int i = 0; i < 100; i++)
{
uncheckItems.Add($"item {i + 1}");
}
var tasks = new List<Task>();
foreach(var item in uncheckedItems) {
tasks.Add(Task.Run(() => ProcessItem(item)));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("========All finished");
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.waitall?view=net-6.0
I'm quite new with programming so the question might look stupid for you:
for (int i = 0; i < 20; i++)
{
driver[i] = new ChromeDriver(service, options[i]);
}
I'm trying to make a webbot. It has to open multiple chrome windows (which works fine)
the part when he opens Chrome is on line 3. Each opening takes like 5-6 seconds.
So my question is, when I initialize a new instance of ChromeDriver, can I continue with the initialization of another ChromeDriver instance (or with other code) even if the first initialization is not done?
tldr; how to initialize multiple instances at the same time if the initialization takes time.
I appreciate any help
You can run multiple threads in C#, which means that you can run your code in parallel, not waiting for one method to complete for other to run.
The recommended way to do that is using methods from System.Threading.Tasks namespace, most notably Task.Run.
Though in your case I would recommend to think of parallel for:
Parallel.For(0, 20, i =>
{
driver[i] = new ChromeDriver(service, options[i]);
});
you can use Task.Run
var task = Task.Run(() => new ChromeDriver(service, options[i]));
The overall code will look like this:
var taskList = new List<Task>(20);
for (int i = 0; i < 20; i++)
{
var task = new ChromeDriver(service, options[i]);
taskList.Add(task);
}
//at the and wait all tasks to finish.
await Task.WaitAll(taskList.ToArray());
We have a processor that will receive a queue of elements, and for every element, it will run some actions that need to be guaranteed to be executed in a sequential manner. Each action to execute on an element is a Promise Task (http://blog.stephencleary.com/2014/04/a-tour-of-task-part-0-overview.html). The processing of each element in the queue doesn't need to wait for completion of the previous one.
The signature of the actions can be assumed to be something like this:
Task MyAwaitableMethod(int delay)
The way I'm seeing, the problem can be reduced to executing a loop, and inside the loop, executing sequential operations, and each iteration shouldn't block. I'm looking at 2 approaches:
1.
for (var i = 0; i < Iterations; i++)
{
Task.Run(async () => {
await MyAwaitableMethod(DelayInMilliseconds);
await MyAwaitableMethod(DelayInMilliseconds);
});
}
2.
for (var i = 0; i < Iterations; i++)
{
MyAwaitableMethod(DelayInMilliseconds).ContinueWith(
antecedent => MyAwaitableMethod(DelayInMilliseconds));
}
I was assuming, given the actions are Promises, that with approach #2, there would be less threads created, as opposed to Task.Run, which I'd assume would create more threads. But in tests I've run, the number of threads created for both when executing a high number of iterations tends to be the same, and not dependent of the given number of iterations.
Are both methods entirely equivalent? Or you guys have better suggestions?
EDIT (rephrasing the question)
Are both methods equivalent in terms of the number of threads both require?
Thanks
Why not use Task.WhenAll()?
var tasks = new List<Task>();
for (var i = 0; i < Iterations; i++)
{
Task t = MyAwaitableMethod(DelayInMilliseconds);
tasks.Add(t);
}
await Task.WhenAll(tasks);
Part of the beauty of async-await is to write sequential asynchronous code.
If it was not to be asynchronous, you would write:
for (var i = 0; i < Iterations; i++)
{
MyAwaitableMethod(DelayInMilliseconds);
MyAwaitableMethod(DelayInMilliseconds);
}
If you want it to be asynchronous, just write:
for (var i = 0; i < Iterations; i++)
{
await MyAwaitableMethod(DelayInMilliseconds);
await MyAwaitableMethod(DelayInMilliseconds);
}
The code you posted does not satisfy your requirement to process each item only after the previous one because you are not awaiting for Task.Run.
I have such particular code:
for (int i = 0; i < SingleR_mustBeWorkedUp._number_of_Requestes; i++)
{
Random myRnd = new Random(SingleR_mustBeWorkedUp._num_path);
while (true)
{
int k = myRnd.Next(start, end);
if (CanRequestBePutted(timeLineR, k, SingleR_mustBeWorkedUp._time_service, start + end) == true)
{
SingleR_mustBeWorkedUp.placement[i] = k;
break;
}
}
}
I use an infinite loop here which will end only if CanRequestBePutted returns true. So how to know that the app isn't responding?
There is a solution by controlling time of working each loop, but it doesn't seem to be really good. And I can't forecast that is going to happen in every cases.
Any solutions?
If you're concerned that this operation could potentially take long enough for the application's user to notice, you should be running it in a non-UI thread. Then you can be sure that it will not be making your application unrepsonsive. You should only be running it in the UI thread if you're sure it will always complete very quickly. When in doubt, go to a non-UI thread.
Don't try to figure out dynamically whether the operation will take a long time or not. If it taking a while is a possibility, do the work in another thread.
Why not use a task or threadpool so you're not blocking and put a timer on it?
The task could look something like this:
//put a class level variable
static object _padlock = new object();
var tasks = new List<Task>();
for (int i = 0; i < SingleR_mustBeWorkedUp._number_of_Requestes; i++)
{
var task = new Task(() =>
{
Random myRnd = new Random(SingleR_mustBeWorkedUp._num_path);
while (true)
{
int k = myRnd.Next(start, end);
if (CanRequestBePutted(timeLineR, k, SingleR_mustBeWorkedUp._time_service, start + end) == true)
{
lock(_padlock)
SingleR_mustBeWorkedUp.placement[i] = k;
break;
}
}
});
task.Start();
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
However I would also try to figure out a way to take out your while(true), which is a bit dangerous. Also Task requires .NET 4.0 or above and i'm not sure what framework your targeting.
If you need something older you can use ThreadPool.
Also you might want to put locks around shared resources like SingleR_mustBeWorkedUp.placement or anywhere else might be changing a variable. I put one around SingleR_mustBeWorkedUp.placement as an example.
I'm making my first steps in parallel programming. I rewrote CalculateSlots to CalculateSlotsAsync. It seams to work fine (3 times faster).
My questions are: Is it written correctly?
Do I need to use the newest async awayt pattern and if yes, how?
private void CalculateSlots(bool isCalculateAllSlots)
{
foreach (IndicatorSlot indicatorSlot in strategy.Slot)
{
if (isCalculateAllSlots || !indicatorSlot.IsCalculated)
CalculateStrategySlot(indicatorSlot.SlotNumber);
}
}
private void CalculateSlotsAsync(bool isCalculateAllSlots)
{
var tasks = new List<Task>();
foreach (IIndicatorSlot indicatorSlot in strategy.Slot)
{
if (isCalculateAllSlots || !indicatorSlot.IsCalculated)
{
IIndicatorSlot slot = indicatorSlot;
Task task = Task.Factory.StartNew(() => CalculateStrategySlot(slot.SlotNumber));
tasks.Add(task);
}
}
Task.WaitAll(tasks.ToArray());
}
Test on i7-3630QM #2.40Gh
// Executed for 96 sec.
for (int i = 0; i < 1000; i++)
CalculateSlots(true);
// Executed for 34 sec.
for (int i = 0; i < 1000; i++)
CalculateSlotsAsync(true);
For data-parallel operations, you can often simplify your implementation by using PLINQ:
strategy.Slot.AsParallel()
.Where(slot => isCalculateAllSlots || !indicatorSlot.IsCalculated)
.ForAll(slot => CalculateStrategySlot(slot.SlotNumber));
However, in your case, each item takes a relatively long time to compute, so I would recommend leaving them as tasks but marking them as LongRunning (which typically has the effect of executing them on a dedicated thread, rather than the thread pool).
Task task = Task.Factory.StartNew(() => CalculateStrategySlot(slot.SlotNumber),
TaskCreationOptions.LongRunning);
Reply: Task.WaitAll causes the calling thread – in your case, the UI thread – to block until all specified tasks have completed. (The behaviour is similar for the PLINQ ForAll.)
In order for your UI to remain responsive, you need to switch from a blocking approach to an asynchronous one. For example, suppose you have:
Task.WaitAll(tasks.ToArray());
UpdateUI(strategy.Slot); // must be called on UI thread
You can replace this with:
Task.Factory.ContinueWhenAll(tasks.ToArray(), completedTasks =>
{
// callback on UI thread
UpdateUI(strategy.Slot);
},
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
In practice, you'll also need to learn how to use CancellationToken to allow the user to discard the operation before it completes.