Finish two tasks then printing something - c#

I have three tasks, one is producer, then consumer and the last one is to print something after finishing the first two. However the code doesn't reach the last task, which means no printing.
while (true)
{
ThreadEvent.WaitOne(waitingTime, false);
lock (SyncVar)
{
collection = new BlockingCollection<string>(4);
Task producer = Task.Run(() =>
{
if (list.Count > 0)
Console.WriteLine("Block begin");
while (!collection.IsAddingCompleted)
{
var firstItem = list.FirstOrDefault();
collection.TryAdd(firstItem);
list.Remove(firstItem);
}
collection.CompleteAdding();
});
Task consumer = Task.Run(() => DoConsume());
Task endTask = consumer.ContinueWith(i => Console.WriteLine("Block end"));// not print this line, why?
Task.WaitAll(producer, consumer, endTask);
if (ThreadState != State.Running) break;
}
}
Please look at my code logic.
EDIT:
For `DoConsume', it is complicated.
public void DoConsume()
{
if (collection.Count > 0)
Console.WriteLine("There are {0} channels to be processed.", collection.Count);
var workItemBlock = new ActionBlock<string>(
workItem =>
{
bool result =ProcessEachChannel(workItem);
});
foreach (var workItem in collection.GetConsumingEnumerable())
{
workItemBlock.Post(workItem);
}
workItemBlock.Complete();
}

The problem is that your producer will never complete:
// This will run until after CompleteAdding is called
while (!collection.IsAddingCompleted)
{
var firstItem = list.FirstOrDefault();
collection.TryAdd(firstItem);
list.Remove(firstItem);
}
//... which doesn't happen until after the loop
collection.CompleteAdding();
It looks like you're just trying to add all of the items in your list, which should be as simple as:
Task producer = Task.Run(() =>
{
if (list.Count > 0)
Console.WriteLine("Block begin");
while(list.Any())
{
var firstItem = list.First();
collection.TryAdd(firstItem);
list.Remove(firstItem);
}
collection.CompleteAdding();
});
Or, a simpler method:
Task producer = Task.Run(() =>
{
if (list.Count > 0)
Console.WriteLine("Block begin");
foreach(var item in list)
{
collection.TryAdd(item);
}
list.Clear();
collection.CompleteAdding();
});

I used Reed Copsey's code but the error is still there. Just can't figure it out why.
I think that my code has the flaw at while (!collection.IsAddingCompleted).
Because the collection has the boundary of 4, suppose there are two item left in the collection. The condition collection.IsAddingCompleted is never met therefore the code could not jump out of the while loop.
I rewrote the code, it seems fine. The code is similar MSDN. I used Take to retrieve the element in the collection.
while (true)
{
ThreadEvent.WaitOne(waitingTime, false);
lock (SyncVar)
{
collection = new BlockingCollection<string>(4);
DoWork dc = new DoWork();
Task consumer = Task.Run(() =>
{
while (!collection.IsCompleted)
{
string data = "";
try
{
if (collection.Count > 0)
data = collection.Take();
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
if (data != "")
{
bool result = dc.DoConsume(data);
}
}
});
Task producer = Task.Run(() =>
{
if (list.Count > 0)
Console.WriteLine("Block begin");
foreach (var item in list)
{
collection.Add(item);
}
list.Clear();
collection.CompleteAdding();
});
Task endTask = consumer.ContinueWith(i => Console.WriteLine("Block end"));
Task.WaitAll(producer, consumer, endTask);
if (ThreadState != State.Running) break;
}

Related

How to Catch Exceptions for an array of Tasks

I would like to understand how what I should be using inside of my "try" block when trying to await an Array of Tasks.
I want all tasks to be awaited, regardless if one of them threw an Exception, so that they can all complete.
Should I use:
var tasks = new Task<CasApiRouterModelExtendedInfo>[mbis.Length];
for (int i = 0; i < mbis.Length; i++)
{
tasks[i] = CAS.Service.GetAllRouterInterfacesAsync(mbis[i], false, 2);
}
try
{
Task.WaitAll(tasks);
}
catch (AggregateException ex)
{
Trace.TraceError("Some interface discoveries failed: ");
foreach (var innerEx in ex.InnerExceptions)
{
Trace.TraceError(innerEx.Message);
}
}
foreach (var task in tasks)
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
returnResults.Add(task.Result);
}
}
OR
var tasks = new Task<CasApiRouterModelExtendedInfo>[mbis.Length];
for (int i = 0; i < mbis.Length; i++)
{
tasks[i] = Task.Run(() => CAS.Service.GetAllRouterInterfacesAsync(mbis[i], true, 2));
}
try
{
for (int i = 0; i < tasks.Length; i++)
{
tasks[i].Wait();
}
}
catch (AggregateException ex)
{
Trace.TraceError("Some interface discoveries failed: ");
foreach (var innerEx in ex.InnerExceptions)
{
Trace.TraceError(innerEx.Message);
}
}
foreach (var task in tasks)
{
if (task.Status == TaskStatus.RanToCompletion && task.Result != null)
{
returnResults.Add(task.Result);
}
}
Also, does that "task.Status == TaskStatus.RanToCompletion" return true as long as the task didn't throw an Exception (is this a good way to do this check)?
Is there any reason you would prefer B? I can't think of one.
B is incorrect in case of error.
Also, it is bad style. Your intention is to wait for all tasks, so say that in the code. No need for manual loops obscuring what you want to accomplish.
Also, does that "task.Status == TaskStatus.RanToCompletion" return true as long as the task didn't throw an Exception (is this a good way to do this check)?
This checks for successful completion. Likely, this is what you want. Tasks can end up cancelled but likely you will consider that as an error case.
task.Result != null
Is that really what you want? task.Result is never set to null by the system. This can only happen if GetAllRouterInterfacesAsync makes the result null.

How to solve "No overload for method, takes 0 arguments?" error

In my simple producer/consumer multi threading project I am using a custom queue collection. I defined my TryDequeue like this...
public bool TryDequeue(out F data)
{
data = default(F);
bool success = false;
lock (_queueLock)
{
if (_queue.Count > 0)
{
data = _queue.Dequeue();
success = true;
count--;
}
}
return success;
}
And when I try this...
private static void Consumer()
{
while (true)
{
Thread.Sleep(100);
string item = null;
lock (sharedQueue)
{
while (sharedQueue.Count == 0)
Monitor.Wait(sharedQueue);
item = sharedQueue.TryDequeue();
}
Console.WriteLine("Consuming item: {0}", item);
}
}
which is a second thread, I get this error:
Error 1 No overload for method 'TryDequeue' takes 0 arguments
Can anyone help me? Thanks.
while (true)
{
Thread.Sleep(100);
string item = null;
lock (sharedQueue)
{
while (sharedQueue.Count == 0)
Monitor.Wait(sharedQueue);
if(sharedQueue.TryDequeue(out item)) //succes!
...
else //something went wrong
...
}
Console.WriteLine("Consuming item: {0}", item);
}
Your method returns a bool, you need to put your item together with the out keyword between the parenthesis. Right now you are not passing the expected out F data parameter.
item = sharedQueue.TryDequeue(); is the culprit. TryDequeue() needs an argument.
Would sharedQueue.TryDequeue(out item); work? That seems to be what you need to do.

Getting weird result while using Task Parallel Library?

I am trying to do some filter task using TPL. Here I am simplifying the code to filter number based on condition. Here is the code.
public static void Main (string[] args)
{
IEnumerable<int> allData = getIntData ();
Console.WriteLine ("Complete Data display");
foreach (var item in allData) {
Console.Write(item);
Console.Write(" | ");
}
Console.WriteLine ();
filterAllDatas (ref allData, getConditions ());
foreach (var item in allData) {
Console.Write(item);
Console.Write(" | ");
}
Console.WriteLine ();
}
static void filterAllDatas(ref IEnumerable<int> data, IEnumerable<Func<int,bool>> conditions)
{
List<int> filteredData = data.ToList ();
List<Task> tasks = new List<Task>();
foreach (var item in data.AsParallel()) {
foreach (var condition in conditions.AsParallel()) {
tasks.Add(Task.Factory.StartNew(() => {
if (condition(item)) {
filteredData.Remove(item);
}
}));
}
}
Task.WaitAll(tasks.ToArray());
data = filteredData.AsEnumerable ();
}
static IEnumerable<Func<int,bool>> getConditions()
{
yield return (a) => { Console.WriteLine("modulo by 2"); return a % 2 == 0;};
yield return (a) => { Console.WriteLine("modulo by 3"); Thread.Sleep(3000); return a % 3 == 0;};
}
static IEnumerable<int> getIntData ()
{
for (int i = 0; i < 10; i++) {
yield return i;
}
}
Here, it is simple code to filter out integer which is divided by two or three. Now, if I remove that thread sleep code work perfectly but if I put that it is not.
Normally means without Thread.Sleep , both condition execute 10 times e.g. for every number. But if I add Thread.Sleep first condition executes 7 times and second executes thirteen times. And because of this few number skip the condition. I try to debug but didn't get anything that can point out issue with my code.
Is there any good way to achieve this? Like filter condition on data can work async and parallel to improve performance ?
Code is for demo purpose only.
FYI: Currently I am using Mono with Xamarine studio on windows machine.
Please let me know if any further details needed.
I would guess it has to do with how your task's lambda closes over the loop variable condition. Try changing it as follows:
foreach (var condition in conditions.AsParallel()) {
var tasksCondition = condition
tasks.Add(Task.Factory.StartNew(() => {
if (tasksCondition(item)) {
filteredData.Remove(item);
}
}));
Note you're also closing over the loop variable item, which could cause similar problems.
First you can change your getConditions method to see what's happening inside :
static IEnumerable<Func<int, bool>> getConditions()
{
yield return (a) => { Console.WriteLine(a + " modulo by 2"); return a % 2 == 0; };
yield return (a) => { Console.WriteLine(a + " modulo by 3"); Thread.Sleep(3000); return a % 3 == 0; };
}
And if you stop capturing the foreach's variables, it will work :
static void filterAllDatas(ref IEnumerable<int> data, IEnumerable<Func<int, bool>> conditions)
{
List<int> filteredData = data.ToList();
List<Task> tasks = new List<Task>();
foreach (var item in data.AsParallel())
{
var i = item;
foreach (var condition in conditions.AsParallel())
{
var c = condition;
tasks.Add(Task.Factory.StartNew(() =>
{
if (c(i))
{
filteredData.Remove(i);
}
}));
}
}
Task.WaitAll(tasks.ToArray());
data = filteredData.AsEnumerable();
}

Chaining Tasks with Different Return Types

Is it possible to chain tasks with different return types or no return type at all? For example in pseudo-code:
Task<double>.ContinueWith(Task<string>).ContinueWith(Task<String>).ContinueWith(Task)
Or also here is real-code example:
private double SumRootN(int root)
{
double result = 0;
for (int i = 1; i < 10000000; i++)
{
result += Math.Exp(Math.Log(i) / root);
}
return result;
}
private void taskSequentialContinuationButton_Click(object sender, RoutedEventArgs e)
{
Task<double> task = null;
this.statusText.Text = "";
this.watch = Stopwatch.StartNew();
for (int i = 2; i < 20; i++)
{
int j = i;
if (task == null)
{
task = Task<double>.Factory.StartNew(() => { return SumRootN(j); });
}
else
{
task = task.ContinueWith((t) => { return SumRootN(j); });
}
task = task.ContinueWith((t) =>
{ // I don't want to return anything from this task but I have to, to get it to compile
this.statusText.Text += String.Format("root {0} : {1}\n", j, t.Result);
return t.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
task.ContinueWith((t) =>
{ // I also don't want to return anything here but I don't seem to have to here even though intellisense expects a Task<double>??
this.statusText.Text += String.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
See the inline comments for the oddities of the chaining.
If you want to chain Tasks with different return types, you can just put each of them in a different variable:
Task<Type1> task1 = Task.Factory.StartNew(() => Compute1());
Task<Type2> task2 = task1.ContinueWith(_ => Compute2());
Task<Type3> task3 = task2.ContinueWith(_ => Compute3());
For your specific case, where you compute something in a loop and want to report on the UI thread after each iteration, you can do it like this:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
for (int i = 2; i < 20; i++)
{
// perform computation
Task.Factory.StartNew(() =>
{
// report result
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
});
Your code is complicated by your re-use of a single task variable. It looks like you want to launch a number of tasks and wait for them to complete. Something like this should work:
SynchronizationContext context = SynchronizationContext.Current;
Task[] tasks = Enumerable.Range(2, 19)
.Select(d => Task<double>.Factory.StartNew(() => SumRootN(d))
.ContinueWith(t => {
this.statusText.Text += string.Format("root {0} : {1}\n", d, t.Result);
}))
.ToArray();
Task.Factory.StartNew(() => {
Task.WaitForAll(tasks);
string msg = string.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds);
context.Post(_ => { this.statusText.Text += msg; }, null);
});
EDIT: If you want to create a chain of tasks, this might work:
Task first = new Task(() => { });
Task outer = Enumerable.Range(2, 19)
.Aggregate(first, (task, d) => {
Task inner = new Task<double>(() => SumRootN(d))
.ContinueWith(rt => {
this.statusText.Text += String.Format("root {0} : {1}\n", d, rt.Result);
});
return task.ContinueWith(inner);
});
outer.ContinueWith(t => {
this.statusText.Text += String.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds);
});
first.Start();

Is Interlocked.Decrement(i) right for Parallel.ForEach()?

In the past I have done this:
List<Item> _Items = GetItems();
int _CountDown = _Items.Count;
using (BackgroundWorker _Worker = new BackgroundWorker())
{
_Worker.DoWork += (s, arg) =>
{
DoSomething(_Items[_CountDown]);
};
_Worker.RunWorkerCompleted += (s, arg) =>
{
if (System.Threading.Interlocked.Decrement(ref _CountDown) == 0)
RaiseAllDoneEvent();
else
_Worker.RunWorkerAsync();
};
_Worker.RunWorkerAsync();
}
With Parallel I do somethign like this:
List<Item> _Items = GetItems();
int _CountDown = _Items.Count;
System.Threading.Tasks.Parallel.ForEach(_Items, (i) =>
{
DoSomething(i);
if (System.Threading.Interlocked.Decrement(ref _CountDown) == 0)
RaiseAllDoneEvent();
});
My real question is: is Interlocked.Decrement() correct here?
Parallel.ForEach will block until all of the items are processed- the right thing to do here seems to be to call RaiseAllDoneEvent() from the caller, immediately after calling Parallel.ForEach:
List<Item> _Items = GetItems();
System.Threading.Tasks.Parallel.ForEach(_Items, (i) =>
{
DoSomething(i);
});
RaiseAllDoneEvent();
In other words, no need to count down (or keep track of how many items there are to process) at all. If you want the parallel operations to not block, you can turn it into a bunch of tasks:
List<Item> _Items = GetItems();
Task.Factory.StartNew(()=> {
Task.WaitAll(
_Items.Select(i => Task.Factory.StartNew(() => DoSomething(i))).ToArray()
);
}).ContinueWith(t => RaiseAllDoneEvent());
In that case, you'd be starting an outer task to spin up and then wait on a bunch of tasks (one for each item), and then finally raise the all done event. None of this will block the original caller.

Categories

Resources