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

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.

Related

Refreshing a UI to reflect items added to a list

While my UI is displayed, data is being passed in the back end and added to a List<string> that I would in turn like to display on my UI.
I've seen several examples using background workers however I don't have access to the actual component due to how I layout my User Controls and programmatically build them.
Question: How can I run this method repeatedly behind my UI without locking up my UI in a loop?
public void UpdatePanel()
{
foreach (var item in list)
{
AddMethod(item);
}
}
Instead of using a loop or time intervals to monitor a list, as an option when possible, you can use a BindingList<T> or ObservableCollection<T> and receive notification when list changes.
Then you can update user interface in the event handler which you attaced to ListChanged event ofBindingList<T> or CollectionChanged event of ObservableCOllection<T>.
Example
Here is an example based on ObservableCollection<string>.
ObservableCollection<string> list;
private void Form1_Load(object sender, EventArgs e)
{
list = new ObservableCollection<string>();
list.CollectionChanged += list_CollectionChanged;
list.Add("Item 1");
list.Add("Item 2");
list.RemoveAt(0);
list[0] = "New Item";
}
void list_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
var items = string.Join(",", e.NewItems.Cast<String>());
MessageBox.Show(string.Format("'{0}' Added", items));
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
var items = string.Join(",", e.OldItems.Cast<String>());
MessageBox.Show(string.Format("'{0}' Removed", items));
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
var oldItems = string.Join(",", e.OldItems.Cast<String>());
var newItems = string.Join(",", e.NewItems.Cast<String>());
MessageBox.Show(string.Format("'{0}' replaced by '{1}'", oldItems, newItems));
}
else
{
MessageBox.Show("Reset or Move");
}
}
You can use Task, Async and await, where is some code which insert an element in a listbox each second without blocking UI.
In your case, you have to return the data from backend asynchronously.
public async void LoadItemsAsync()
{
for (int i = 0; i < 10; i++)
listBox1.Items.Add(await GetItem());
}
public Task<string> GetItem()
{
return Task<string>.Factory.StartNew(() =>
{
Thread.Sleep(1000);
return "Item";
});
}

Why my Task.ContinueWith not wait the define time

I am using this method to open specific work with concurrent Threads from my Main UI thread:
private List<MyData> MyCollection;
private static CancellationTokenSource _tokenSource;
private void Start()
{
int concurrentThread = (int)nudConcurrentFiles.Value;
int loops = (int)nudLoops.Value;
var token = _tokenSource.Token;
Task.Factory.StartNew(() =>
{
try
{
while (Iteration.LoopFinished < loops)
{
Parallel.ForEach(PcapList.Files,
new ParallelOptions
{
MaxDegreeOfParallelism = concurrentThread //limit number of parallel threads
},
File=>
{
if (token.IsCancellationRequested)
return;
//do work...
});
Iteration.LoopFinished++;
Task.Delay(10000).ContinueWith(
t =>
{
}, _tokenSource.Token);
}
}
catch (Exception e)
{ }
}, _tokenSource.Token,
TaskCreationOptions.None,
TaskScheduler.Default).ContinueWith(
t =>
{
}
);
}
The problem is that after loop i want to wait 10 secons and Task.Delay(10000).ContinueWith not waiting this 10 seconds but start immedietly another loop.
You need to call Wait() method in order to execute the task
Task.Delay(10000).ContinueWith(
t =>
{
}, _tokenSource.Token).Wait();

Finish two tasks then printing something

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;
}

Convert Event based code to Rx

I have the following code (simplified for posting purposes).
public class SomeDataObject
{
public delegate void ReadyEventHandler;
public delegate void ErrorEventHandler;
public event ReadyEventHandler Ready;
public event ErrorEventHandler Error;
...
}
pubic class ConsumerClass
{
private SomeDataObject dataObject;
private Task<List<string>> GetStrings()
{
List<string> results = new List<string>();
var tcs = new TaskCompletionSource<List<string>>();
SomeDataObject.ReadyEventHandler ReadyHandler = null;
SomeDataObject.ErrorEventHandler ErrorHandler = null;
ReadyHandler += () =>
{
for (int i =0; i < dataObject.ItemCount; i++)
results.Add(dataObject[i].ToString());
tcs.TrySetResult(results);
}
ErrorHandler += ()
{
tcs.TrySetException(new Exception("oops!");
}
dataObject.Ready += ReadyHandler;
dataObject.Error += ErrorHandler;
dataObject.DoRequest();
}
}
The idea is that when DoRequest call is made, SomeDataObject will get some data and raise either the Ready or Error events (details not important!). If data is available, then the ItemCount indicates how many items are available.
I am new to Rx and cannot find any comparable example. So is it possible to convert this into Rx so that IObservable<string> is returned instead of Task<List<string>> using Observable.Create somehow?
Regards
Alan
Matthew's answer is close but has some problems. First, it is eager, which is not normally in the spirit of Rx/Functional programming. Next I think that you will want to be able to release the event handles when the consumer disposes. Finally the usage of a subject should be a code smell, and this case it points to the two problems above :-)
Here I use Observable.Create (which should be your #1 goto tool in the tool box, with subjects being your last resort) to lazily connect, and also offer disconnection/releasing events when the subscription is disposed.
private IObservable<string> GetStrings()
{
return Observable.Create<string>(o=>
{
SomeDataObject.ReadyEventHandler ReadyHandler = null;
SomeDataObject.ErrorEventHandler ErrorHandler = null;
ReadyHandler += () =>
{
for (int i =0; i < dataObject.ItemCount; i++)
o.OnNext(dataObject[i].ToString());
o.OnCompleted();
}
ErrorHandler += () =>
{
o.OnError(new Exception("oops!"));
}
dataObject.Ready += ReadyHandler;
dataObject.Error += ErrorHandler;
dataObject.DoRequest();
return Disposable.Create(()=>
{
dataObject.Ready -= ReadyHandler;
dataObject.Error -= ErrorHandler;
});
}
}
I would also consider moving dataObject to a parameter to the method too. Sharing state in an Async system is a source of problems.
In response to your comments on Lee's (perfectly lovely and tick-worthy) answer, here's how to modify his answer to get a single List<string> response and block for it:
private IObservable<List<string>> GetStrings(SomeDataObject dataObject)
{
return Observable.Create<List<string>>(o=>
{
SomeDataObject.ReadyEventHandler ReadyHandler = null;
SomeDataObject.ErrorEventHandler ErrorHandler = null;
ReadyHandler = () =>
{
var results = new List<string>(dataObject.ItemCount);
for (int i =0; i < dataObject.ItemCount; i++)
results.Add(dataObject[i].ToString());
o.OnNext(results);
o.OnCompleted();
};
ErrorHandler = () =>
{
o.OnError(new Exception("oops!"));
};
dataObject.Ready += ReadyHandler;
dataObject.Error += ErrorHandler;
dataObject.DoRequest();
return Disposable.Create(()=>
{
dataObject.Ready -= ReadyHandler;
dataObject.Error -= ErrorHandler;
});
});
}
Now you can block on this with:
var results = GetStrings().Wait();
If using .NET 4.5, then in an async method you can also do:
var results = await GetStrings();
I think the code below will do what you want. A ReplaySubject is used to ensure that the caller gets all of the results, even if the SomeDataObject events start immediately.
private IObservable<string> GetStrings()
{
ReplaySubject<string> results = new ReplaySubject<string>();
SomeDataObject.ReadyEventHandler ReadyHandler = null;
SomeDataObject.ErrorEventHandler ErrorHandler = null;
ReadyHandler += () =>
{
for (int i =0; i < dataObject.ItemCount; i++)
results.OnNext(dataObject[i].ToString());
results.OnCompleted();
}
ErrorHandler += ()
{
results.OnError(new Exception("oops!"));
}
dataObject.Ready += ReadyHandler;
dataObject.Error += ErrorHandler;
dataObject.DoRequest();
return results;
}

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();

Categories

Resources