C# property assign bug - c#

I'm working on a WPF-MVVM project and I implement asynchronous infinite loops in some background threads. What I have done in the ViewModel is
public TestVM()
{
LineIO_Task();
//some other work
}
and LineIO_Task is defined as
public void LineIO_Task()
{
foreach (Line l in Lines)
{
Task GetP = new Task(() => { EnPost(l); }, TaskCreationOptions.LongRunning);
GetP.Start();
//EnPost(Lines[i]);
}
}
Lines is an ObservableCollection that is initialized in TestVm.There are 7 items in Lines. And EnPost is defined as
public async void EnPost(Line l)
{
try
{
int last = 0;
while (true)
{
int pno = l.Com.ReadPostNo();//read a serial port
if (pno != last && pno != -1 && pno != 0)
{
Dispatcher.Invoke(() =>
{
Post p = AssignedPost.First(x => x.Num == pno);
if (p == null)
{
LocalDb.InsertPost(l.Num, new Post(pno));
}
else
{
if (p.ToLine != l.Num)
{
//update UI
Lines.First(n => n.Num == p.ToLine).Posts.First(y => y.Num == p.Num).IsValid = true;
Lines.First(n => n.Num == p.ToLine).OnPropertyChanged("RealPostCount");
}
}
});
last = pno;
}
await Task.Delay(150);
}
}
catch (Exception ex)
{
//
}
}
AssignedPost is an ObservableCollection of Post. Post has int properties ToLine and Num.
And I have a function called Assign, which add items into AssignedPost.
private int Assign(Post p)
{
int ln;
//evaluate ln by some algorithm
p.ToLine = ln;
AssignedPost.Add(p);
//some other work
return ln;
}
The problem is that the item has been added into AssignedPost, i.e., Post p = AssignedPost.First(x => x.Num == pno); p is not null. But p.ToLine, which is supposed to be assigned before the insertion, is always 0. So the bug makes the UI update raises an error. I can't see why. Does it have something to do with multithreading?

You cannot touch the UI thread from any other thread or you run into problems.
When you want to update something for the UI you need to do:
View.Dispatcher.InvokeAsync(a.Invoke, p);
Which will marshall the call to the UI thread to avoid this.
If your using something like an ObservableCollection you can use collection synchronization to call it from another thread and the calls to INotifyPropertyChanged will be marshalled to the UI thread for you.
BindingOperations.EnableCollectionSynchronization(Entries);
Just make sure to turn it off when your done to avoid a memory leak like so:
BindingOperations.DisableCollectionSynchronization(Entries);

Related

Fill List<object> in a new thread and get it back to the UI Thread

I got following function generating a List of LinesVisual3D objects. I need to do this in a new task, because it's very time consuming.
public async Task<List<LinesVisual3D>> Create2dGcodeLayerModelListAsync(IProgress<int> prog = null)
{
var list = new List<LinesVisual3D>();
try
{
await Task.Factory.StartNew(delegate ()
{
var temp = new List<LinesVisual3D>();
int i = 0;
foreach (List<GCodeCommand> commands in Model.Commands)
{
var line = drawLayer(i, 0, Model.Commands[i].Count, false);
/*
if (DispatcherObject.Thread != Thread.CurrentThread)
DispatcherObject.BeginInvoke(new Action(() => list.Add(drawLayer(i, 0, Model.Commands[i].Count, false))));
*/
temp.Add(drawLayer(i, 0, Model.Commands[i].Count, false));
if (prog != null)
{
float test = (((float)i / Model.Commands.Count) * 100f);
if (i < Model.Commands.Count - 1)
prog.Report(Convert.ToInt32(test));
else
prog.Report(100);
}
i++;
}
list = new List<LinesVisual3D>(temp);
});
LayerModelGenerated = true;
return list;
}
catch (Exception exc)
{
return list;
}
}
It actually works fine, however I do not get the list out of the background thread. When I access the list in the UI (different thread), I'll get this result:
I know that the problem is, that the list was filled / generated in a different thread (in this case, ThreadId = 3). However the UI is running in ThreadId = 1
I already tried to invoke directly after the loop has finished.
//DispatcherObject = Dispatcher.CurrentDispatcher;
if (DispatcherObject.Thread != Thread.CurrentThread)
DispatcherObject.BeginInvoke(new Action(() => list = new List<LinesVisual3D>(temp)));
else
list = new List<LinesVisual3D>(temp);
I also tried to invoke while adding to the list.
if (DispatcherObject.Thread != Thread.CurrentThread)
DispatcherObject.BeginInvoke(new Action(() => list.Add(drawLayer(i, 0, Model.Commands[i].Count, false))));
The result always was the same.
EDIT1:
Tried with single instance instead of list.
public async Task<LinesVisual3D> Create2dGcodeLayerAsync(IProgress<int> prog = null)
{
var temp = new LinesVisual3D();
try
{
await Task.Factory.StartNew(delegate ()
{
if (DispatcherObject.Thread != Thread.CurrentThread)
DispatcherObject.BeginInvoke(new Action(() => temp = drawLayer(3, 0, Model.Commands[3].Count, false)));
//temp = drawLayer(3, 0, Model.Commands[3].Count, false);
});
LayerModelGenerated = true;
return temp;
}
catch (Exception exc)
{
return temp;
}
}
This seems to work. I guess becuase the temp object is generated outside the new task, however the object line in the task?
EDIT2:
This function works, however freezes the UI...
public async Task<List<LinesVisual3D>> Create2dGcodeLayerModelListAsync(IProgress<int> prog = null)
{
var list = new List<LinesVisual3D>();
var line = new LinesVisual3D();
try
{
await Task.Factory.StartNew(delegate ()
{
var temp = new List<LinesVisual3D>();
int i = 0;
foreach (List<GCodeCommand> commands in Model.Commands)
{
// Freezes the UI...
if (DispatcherObject.Thread != Thread.CurrentThread)
{
DispatcherObject.Invoke(new Action(() =>
{
line = drawLayer(i, 0, Model.Commands[i].Count, false);
list.Add(line);
}));
}
if (prog != null)
{
float test = (((float)i / Model.Commands.Count) * 100f);
if (i < Model.Commands.Count - 1)
prog.Report(Convert.ToInt32(test));
else
prog.Report(100);
}
i++;
}
});
LayerModelGenerated = true;
return list;
}
catch (Exception exc)
{
return list;
}
}
Either it works and freezes the UI, or it leaves the list in the old thread and doesn't freeze the UI :(
Solution:
Instead of creating the LinesVisual3D object in the loop, I just create a List of Point3D and create a new LinesVisual3D at the Invoke.
public async Task<List<LinesVisual3D>> Create2dGcodeLayerModelListAsync(IProgress<int> prog = null)
{
var list = new List<LinesVisual3D>();
var line = new LinesVisual3D();
try
{
await Task.Factory.StartNew(delegate ()
{
var temp = new List<LinesVisual3D>();
int i = 0;
foreach (List<GCodeCommand> commands in Model.Commands)
{
var pointsPerLayer = getLayerPointsCollection(i, 0, Model.Commands[i].Count, false);
if (DispatcherObject.Thread != Thread.CurrentThread)
{
DispatcherObject.Invoke(new Action(() =>
{
line = new LinesVisual3D() { Points = new Point3DCollection(pointsPerLayer)};
list.Add(line);
}));
}
if (prog != null)
{
float test = (((float)i / Model.Commands.Count) * 100f);
if (i < Model.Commands.Count - 1)
prog.Report(Convert.ToInt32(test));
else
prog.Report(100);
}
i++;
}
});
LayerModelGenerated = true;
return list;
}
catch (Exception exc)
{
return list;
}
}
Points:
private List<Point3D> getLayerPointsCollection(int layerNumber, int fromProgress, int toProgress, bool isNextLayer)
{
...
}
I do not get the list out of the background thread. When I access the list in the UI (different thread), I'll get this result:
You get the list out of the background thread, what causes problems is accessing the properties of the individual list items.
Most likely, those items are ui objects themselves and have to be created on the ui thread. So you have to dispatch each new back to the ui thread, which essentially leaves adding them to the list as only job for the background task, which will hardly be cpu-bound, so just drop the background thread.
at a guess I'd say the initial call to Create2dGcodeLayerModelListAsync returns the first empty instance of list, when the inner thread starts it also creates a new instance of list in fact two new instances as the last line initialises a new instance with temp, the population of these instances are never returned from Create2dGcodeLayerModelListAsync.
try replace
temp.Add(drawLayer(i, 0, Model.Commands[i].Count, false));
with
list.Add(drawLayer(i, 0, Model.Commands[i].Count, false));
and delete
list = new List<LinesVisual3D>(temp);
As the call to Create2dGcodeLayerModelListAsync is async (await ..) it will spin up a worker thread anyway the inner thread is redundant and waists time spinning up yet another thread. but start by getting rid of the news first.

Make using statement usable for multiple disposable objects

I have a bunch of text files in a folder, and all of them should have identical headers. In other words the first 100 lines of all files should be identical. So I wrote a function to check this condition:
private static bool CheckHeaders(string folderPath, int headersCount)
{
var enumerators = Directory.EnumerateFiles(folderPath)
.Select(f => File.ReadLines(f).GetEnumerator())
.ToArray();
//using (enumerators)
//{
for (int i = 0; i < headersCount; i++)
{
foreach (var e in enumerators)
{
if (!e.MoveNext()) return false;
}
var values = enumerators.Select(e => e.Current);
if (values.Distinct().Count() > 1) return false;
}
return true;
//}
}
The reason I am using enumerators is memory efficiency. Instead of loading all file contents in memory I enumerate the files concurrently line-by-line until a mismatch is found, or all headers have been examined.
My problem is evident by the commented lines of code. I would like to utilize a using block to safely dispose all the enumerators, but unfortunately using (enumerators) doesn't compile. Apparently using can handle only a single disposable object. I know that I can dispose the enumerators manually, by wrapping the whole thing in a try-finally block, and running the disposing logic in a loop inside finally, but is seems awkward. Is there any mechanism I could employ to make the using statement a viable option in this case?
Update
I just realized that my function has a serious flaw. The construction of the enumerators is not robust. A locked file can cause an exception, while some enumerators have already been created. These enumerators will not be disposed. This is something I want to fix. I am thinking about something like this:
var enumerators = Directory.EnumerateFiles(folderPath)
.ToDisposables(f => File.ReadLines(f).GetEnumerator());
The extension method ToDisposables should ensure that in case of an exception no disposables are left undisposed.
You can create a disposable-wrapper over your enumerators:
class DisposableEnumerable : IDisposable
{
private IEnumerable<IDisposable> items;
public event UnhandledExceptionEventHandler DisposalFailed;
public DisposableEnumerable(IEnumerable<IDisposable> items) => this.items = items;
public void Dispose()
{
foreach (var item in items)
{
try
{
item.Dispose();
}
catch (Exception e)
{
var tmp = DisposalFailed;
tmp?.Invoke(this, new UnhandledExceptionEventArgs(e, false));
}
}
}
}
and use it with the lowest impact to your code:
private static bool CheckHeaders(string folderPath, int headersCount)
{
var enumerators = Directory.EnumerateFiles(folderPath)
.Select(f => File.ReadLines(f).GetEnumerator())
.ToArray();
using (var disposable = new DisposableEnumerable(enumerators))
{
for (int i = 0; i < headersCount; i++)
{
foreach (var e in enumerators)
{
if (!e.MoveNext()) return false;
}
var values = enumerators.Select(e => e.Current);
if (values.Distinct().Count() > 1) return false;
}
return true;
}
}
The thing is you have to dispose those objects separately one by one anyway. But it's up to you where to encapsulate that logic. And the code I've suggested has no manual try-finally,)
To the second part of the question. If I get you right this should be sufficient:
static class DisposableHelper
{
public static IEnumerable<TResult> ToDisposable<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector) where TResult : IDisposable
{
var exceptions = new List<Exception>();
var result = new List<TResult>();
foreach (var i in source)
{
try { result.Add(selector(i)); }
catch (Exception e) { exceptions.Add(e); }
}
if (exceptions.Count == 0)
return result;
foreach (var i in result)
{
try { i.Dispose(); }
catch (Exception e) { exceptions.Add(e); }
}
throw new AggregateException(exceptions);
}
}
Usage:
private static bool CheckHeaders(string folderPath, int headersCount)
{
var enumerators = Directory.EnumerateFiles(folderPath)
.ToDisposable(f => File.ReadLines(f).GetEnumerator())
.ToArray();
using (new DisposableEnumerable(enumerators))
{
for (int i = 0; i < headersCount; i++)
{
foreach (var e in enumerators)
{
if (!e.MoveNext()) return false;
}
var values = enumerators.Select(e => e.Current);
if (values.Distinct().Count() > 1) return false;
}
return true;
}
}
and
try
{
CheckHeaders(folderPath, headersCount);
}
catch(AggregateException e)
{
// Prompt to fix errors and try again
}
I'm going to suggest an approach that uses recursive calls to Zip to allow parallel enumeration of a normal IEnumerable<string> without the need to resort to using IEnumerator<string>.
bool Zipper(IEnumerable<IEnumerable<string>> sources, int take)
{
IEnumerable<string> ZipperImpl(IEnumerable<IEnumerable<string>> ss)
=> (!ss.Skip(1).Any())
? ss.First().Take(take)
: ss.First().Take(take).Zip(
ZipperImpl(ss.Skip(1)),
(x, y) => (x == null || y == null || x != y) ? null : x);
var matching_lines = ZipperImpl(sources).TakeWhile(x => x != null).ToArray();
return matching_lines.Length == take;
}
Now build up your enumerables:
IEnumerable<string>[] enumerables =
Directory
.EnumerateFiles(folderPath)
.Select(f => File.ReadLines(f))
.ToArray();
Now it's simple to call:
bool headers_match = Zipper(enumerables, 100);
Here's a trace of running this code against three files with more than 4 lines:
Ben Petering at 5:28 PM ACST
Ben Petering at 5:28 PM ACST
Ben Petering at 5:28 PM ACST
From a call 2019-05-23, James mentioned he’d like the ability to edit the current shipping price rules (eg in shipping_rules.xml) via the admin.
From a call 2019-05-23, James mentioned he’d like the ability to edit the current shipping price rules (eg in shipping_rules.xml) via the admin.
From a call 2019-05-23, James mentioned he’d like the ability to edit the current shipping price rules (eg in shipping_rules.xml) via the admin.
He also mentioned he’d like to be able to set different shipping price rules for a given time window, e.g. Jan 1 to Jan 30.
He also mentioned he’d like to be able to set different shipping price rules for a given time window, e.g. Jan 1 to Jan 30.
He also mentioned he’d like to be able to set different shipping price rules for a given time window, e.g. Jan 1 to Jan 30.
These storyishes should be considered when choosing the appropriate module to use.
These storyishes should be considered when choosing the appropriate module to use.X
These storyishes should be considered when choosing the appropriate module to use.
Note that the enumerations stop when they encountered a mismatch header in the 4th line on the second file. All enumerations then stopped.
Creating an IDisposable wrapper as #Alex suggested is correct. It needs just a logic to dispose already opened files if some of them is locked and probably some logic for error states. Maybe something like this (error state logic is very simple):
public class HeaderChecker : IDisposable
{
private readonly string _folderPath;
private readonly int _headersCount;
private string _lockedFile;
private readonly List<IEnumerator<string>> _files = new List<IEnumerator<string>>();
public HeaderChecker(string folderPath, int headersCount)
{
_folderPath = folderPath;
_headersCount = headersCount;
}
public string LockedFile => _lockedFile;
public bool CheckFiles()
{
_lockedFile = null;
if (!TryOpenFiles())
{
return false;
}
if (_files.Count == 0)
{
return true; // Not sure what to return here.
}
for (int i = 0; i < _headersCount; i++)
{
if (!_files[0].MoveNext()) return false;
string currentLine = _files[0].Current;
for (int fileIndex = 1; fileIndex < _files.Count; fileIndex++)
{
if (!_files[fileIndex].MoveNext()) return false;
if (_files[fileIndex].Current != currentLine) return false;
}
}
return true;
}
private bool TryOpenFiles()
{
bool result = true;
foreach (string file in Directory.EnumerateFiles(_folderPath))
{
try
{
_files.Add(File.ReadLines(file).GetEnumerator());
}
catch
{
_lockedFile = file;
result = false;
break;
}
}
if (!result)
{
DisposeCore(); // Close already opened files.
}
return result;
}
private void DisposeCore()
{
foreach (var item in _files)
{
try
{
item.Dispose();
}
catch
{
}
}
_files.Clear();
}
public void Dispose()
{
DisposeCore();
}
}
// Usage
using (var checker = new HeaderChecker(folderPath, headersCount))
{
if (!checker.CheckFiles())
{
if (checker.LockedFile is null)
{
// Error while opening files.
}
else
{
// Headers do not match.
}
}
}
I also removed .Select() and .Distinct() when checking the lines. The first just iterates over the enumerators array - the same as foreach above it, so you are enumerating this array twice. Then creates a new list of lines and .Distinct() enumerates over it.

C# Filter Items In A List According To Multiple Criteria

First, what my situation here is...
My SomeObject has a property string Status which I am interested in for this scenario.
Status property can contain "Open", "Closed", "Finished" values exactly.
I have a method called FilterObjects which returns a List<SomeObject>
Method accepts an argument same as its return type, List<SomeObject>
Method is supposed to filter according to following cases explained below and return the list of objects.
The List<SomeObject> I am sending as argument to my method is guaranteed to be in order (through their ID and type).
The cases are (all related to the string Status property I mentioned):
If any item in the list contains Status = "Finished"; then eliminate all other elements that was in the original list and return only the object that has the "Finished" status.
If any item does NOT contain Status = Finished but contains "CLOSED", I need to check if there is any other item that has the value of "Open" after that "CLOSED" one. You can think of this as a "a task can be closed, but can be reopened. But once it is finished, it cannot be reopened".
If it contains a "CLOSED" and does not have any "OPEN" after that item, I will ignore all the items before CLOSED and only return CLOSED object. If it contains "OPEN" after any closed, I need to return anything AFTER that CLOSED, by excluding itself.
I also tried explain the same thing with my awesome MS Paint skills.
The object itself is not really a problem, but my method is something like this:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
var objects = objectList;
var returnList = new List<SomeObject>();
foreach (var obj in objects)
{
if (obj.Status == "Finished")
{
returnList.Add(obj);
return returnList;
}
}
return new List<SomeObject>();
}
Long story short, what would be the best and most efficient way to apply all this logic in this single method? Honestly, I couldn't go further than the first case I already implemented, which is the FINISHED. Could this whole thing be done with some LINQ magic?
It is guaranteed that I receive an ordered list AND I will never get items more than a couple of hundred so the collection will never be massive.
Many thanks in advance for the help.
You can try something like that:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
SomeObject finished = objectList.FirstOrDefault(o => o.Status.Equals("Finished"));
if (finished != null) { return new List<SomeObject> { finished }; }
List<SomeObject> closed = objectList.SkipWhile(o => !o.Status.Equals("Closed")).ToList();
if (closed.Count == 1) { return closed; }
if (closed.Count > 1) { return closed.Skip(1).ToList(); }
// if you need a new list object than return new List<SomeObject>(objectList);
return objectList;
}
I really wouldn't bother using Linq for this, as you will either create an overly complicated instruction to manage or you will require several loop iterations. I would go for something like this instead:
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int lastClosed = -1;
for (int i = 0; i < objectList.Count; i++)
{
if (objectList[i].Status == "Closed")
lastClosed = i;
else if (objectList[i].Status == "Finished")
return new List<SomeObject>() { objectList[i] };
}
if (lastClosed > -1)
if (lastClosed == objectList.Count - 1)
return new List<SomeObject>() { objectList[lastClosed] };
else
return objectList.Skip(lastClosed + 1).ToList();
else
return objectList;
}
EDIT: slightly changed the last bit of code so that it won't trigger an exception if the objectList is empty
LINQ is not well suited and inefficient for scenarios where you need to apply logic based on previous / next elements of a sequence.
The optimal way to apply your logic is to use a single loop and track the Closed status and the position where the status change occurred. At the end you'll return a single element at that position if the last status is Closed, or a range starting at that position otherwise.
static List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
int pos = 0;
bool closed = false;
for (int i = 0; i < objectList.Count; i++)
{
var item = objectList[i];
if (item.Status == "Finished")
return new List<SomeObject> { item };
if (item.Status == (closed ? "Opened" : "Closed"))
{
pos = i;
closed = !closed;
}
}
return objectList.GetRange(pos, closed ? 1 : objectList.Count - pos);
}
I did it this way:
public static IEnumerable<SomeObject> convert(this IEnumerable<SomeObject> input)
{
var finished = input.FirstOrDefault(x => x.Status == "Finished");
if (finished != null)
{
return new List<SomeObject> {finished};
}
return input.Aggregate(new List<SomeObject>(), (a, b) =>
{
if (!a.Any())
{
a.Add(b);
}
else if (b.Status == "Open")
{
if (a.Last().Status == "Closed")
{
a.Remove(a.Last());
}
a.Add(b);
}
else if (b.Status == "Closed")
{
a = new List<SomeObject> {b};
}
return a;
});
}
You can write a method like this. This is bare minimum you will have to add null check and exception handling.
public List<SomeCls> GetResult(List<SomeCls> lstData)
{
List<SomeCls> lstResult;
if(lstData.Any(x=>x.Status=="Finished"))
{
lstResult = lstData.Where(x=>x.Status=="Finished").ToList();
}
else if(lstData.Any(x=>x.Status=="Closed"))
{
// Here assuming that there is only one Closed in whole list
int index = lstData.FindIndex(0,lstData.Count(),x=>x.Status=="Closed");
lstResult = lstData.GetRange(index,lstData.Count()-index);
if(lstResult.Count()!=1) // check if it contains Open.
{
lstResult = lstResult.Where(x=>x.Status=="Open").ToList();
}
}
else // Only Open
{
lstResult = lstData;
}
return lstResult;
}
something like this :
private List<SomeObject> FilterObjects(List<SomeObject> objectList)
{
if (objectList.Where(x => x.Status == "Finished").Any())
{
return objectList.Where(x => x.Status == "Finished").ToList();
}
else if (objectList.Where(x => x.Status == "Closed").Any())
{
if (objectList.FindIndex(x => x.Status == "Closed") == objectList.Count() - 1)
{
return objectList.Where(x => x.Status == "Closed").ToList();
}
else
{
return objectList.GetRange(objectList.FindIndex(x => x.Status == "Closed") + 1, objectList.Count() - (objectList.FindIndex(x => x.Status == "Closed") + 1));
}
}
return objectList;
}

C# infinitive task loop using Task<> class + cancellation

I`m trying to make a small class for the multithreading usage in my WinForm projects.
Tried Threads(problems with UI), Backgroundworker(smth went wrong with UI too, just leave it now:)), now trying to do it with Task class. But now, can`t understand, how to make an infinitive loop and a cancelling method (in class) for all running tasks.
Examples i found is to be used in 1 method.
So, here is a structure & code of currently working part (Worker.css and methonds used in WinForm code).
Worker.css
class Worker
{
public static int threadCount { get; set; }
public void doWork(ParameterizedThreadStart method)
{
Task[] tasks = Enumerable.Range(0, 4).Select(i => Task.Factory.StartNew(() => method(i))).ToArray();
}
}
usage on
Form1.cs
private void Start_btn_Click(object sender, EventArgs e)
{
Worker.threadCount = 1; //actually it doesn`t using now, number of tasks is declared in class temporaly
Worker worker = new Worker();
worker.doWork(Job);
string logString_1 = string.Format("Starting {0} threads...", Worker.threadCount);
log(logString_1);
}
public static int j = 0;
private void Job(object sender)
{
Worker worker = new Worker();
Random r = new Random();
log("Thread "+Thread.CurrentThread.ManagedThreadId +" is working...");
for (int i = 0; i < 5; i++)
{
j++;
log("J==" + j);
if (j == 50)
{
//worker.Stop();
log("STOP");
}
}
Thread.Sleep(r.Next(500, 1000));
}
So, it run an example 4 threads, they executed, i got J==20 in my log, it`s ok.
My question is, how to implement infinitive loop for the tasks, created by Worker.doWork() method.
And also to make a .Stop() method for the Worker class (which should just stop all tasks when called). As i understand it`s related questions, so i put it in 1.
I tryed some solutions, but all of them based on the CancellationToken usage, but i have to create this element only inside of the Worker.doWork() method, so i can`t use the same token to create a Worker.Stop() method.
Someone can help? threads amount range i have to use in this software is about 5-200 threads.
using J computation is just an example of the the easy condition used to stop a software work(stop of tasks/threads).
In real, stop conditions is mostly like Queue<> is finished, or List<> elements is empty(finished).
Finally, get it works.
class Worker
{
public static int threadCount { get; set; }
Task[] tasks;
//ex data
public static string exception;
static CancellationTokenSource wtoken = new CancellationTokenSource();
CancellationToken cancellationToken = wtoken.Token;
public void doWork(ParameterizedThreadStart method)
{
try
{
tasks = Enumerable.Range(0, 4).Select(i => Task.Factory.StartNew(() =>
{
while (!cancellationToken.IsCancellationRequested)
{
method(i);
}
}, cancellationToken)).ToArray();
}
catch (Exception ex) { exception = ex.Message; }
}
public void HardStop()
{
try
{
using (wtoken)
{
wtoken.Cancel();
}
wtoken = null;
tasks = null;
}
catch (Exception ex) { exception = ex.Message; }
}
}
But if i`m using this method to quit cancellationToken.ThrowIfCancellationRequested();
Get a error:
when Job() method reach J == 50, and worker.HardStop() function called, program window crashes and i get and exception "OparetionCanceledException was unhandled by user code"
on this string
cancellationToken.ThrowIfCancellationRequested();
so, whats wrong? i`m already put it in try{} catch(){}
as i understood, just some boolean properties should be changed in Task (Task.IsCancelled == false, Task.IsFaulted == true) on wtoken.Cancel();
I'd avoid all of the mucking around with tasks and use Microsoft's Reactive Framework (NuGet "Rx-Main") for this.
Here's how:
var r = new Random();
var query =
Observable
.Range(0, 4, Scheduler.Default)
.Select(i =>
Observable
.Generate(0, x => true, x => x, x => x,
x => TimeSpan.FromMilliseconds(r.Next(500, 1000)),
Scheduler.Default)
.Select(x => i))
.Merge();
var subscription =
query
.Subscribe(i => method(i));
And when you want to cancel the calls to method just do this:
subscription.Dispose();
I've tested this and it works like a treat.
If I wrap this up in your worker class then it looks like this:
class Worker
{
private Random _r = new Random();
private IDisposable _subscription = null;
public void doWork()
{
_subscription =
Observable
.Range(0, 4, Scheduler.Default)
.Select(n =>
Observable
.Generate(
0, x => true, x => x, x => x,
x => TimeSpan.FromMilliseconds(_r.Next(500, 1000)),
Scheduler.Default)
.Select(x => n))
.Merge()
.Subscribe(i => method(i));
}
public void HardStop()
{
_subscription.Dispose();
}
}

Parallel.For loop for spell checking using NHunspell and c#

I have a list of string and if I run spell checking using NHunspell in sequential manner then everything works fine; but if I use Parallel.For loop against the List the application stops working in the middle( some address violation error )
public static bool IsSpellingRight(string inputword, byte[] frDic, byte[] frAff, byte[] enDic, byte[] enAff)
{
if (inputword.Length != 0)
{
bool correct;
if (IsEnglish(inputword))
{
using (var hunspell = new Hunspell(enAff, enDic))
{
correct = hunspell.Spell(inputword);
}
}
else
{
using (var hunspell = new Hunspell(frAff, frDic))
{
correct = hunspell.Spell(inputword);
}
}
return correct ;
}
return false;
}
Edit:
var tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;
var poptions = new ParallelOptions();
// Keep one core/CPU free...
poptions.MaxDegreeOfParallelism = Environment.ProcessorCount - 1;
Task task = Task.Factory.StartNew(delegate
{
Parallel.For(0, total, poptions, i =>
{
if (words[i] != "")
{
_totalWords++;
if (IsSpellingRight(words[i],dictFileBytes,
affFileBytes,dictFileBytesE,affFileBytesE))
{
// do something
}
else
{
BeginInvoke((Action) (() =>
{
//do something on UI thread
}));
}
}
});
}, tokenSource.Token);
task.ContinueWith((t) => BeginInvoke((Action) (() =>
{
MessaageBox.Show("Done");
})));
I think you should forget about your parallel loop implement the things right.
Are you aware of the fact that this code loads and constructs the dictionary:
using (var hunspell = new Hunspell(enAff, enDic))
{
correct = hunspell.Spell(inputword);
}
Your are loading and construction the dictionary over and over again with your code. This is awfully slow! Load Your dictionary once and check all words, then dispose it. And don't do this in parallel because Hunspell objects are not thread safe.
Pseodocode:
Hunspell hunspell = null;
try
{
hunspell = new Hunspell(enAff, enDic)
for( ... )
{
hunspell.Spell(inputword[i]);
}
}
}
finally
{
if( hunspell != null ) hunspell.Dispose();
}
If you need to check words massive in parallel consider to read this article:
http://www.codeproject.com/Articles/43769/Spell-Check-Hyphenation-and-Thesaurus-for-NET-with
Ok, now I can see a potential problem. In line
_totalWords++;
you're incrementing a value, that (I suppose) is declared somewhere outside the loop. Use locking mechanism.
edit:
Also, you could use Interlocked.Increment(ref val);, which would be faster, than simple locking.
edit2:
Here's how the locking I described in comment should look like for the problem you encounter:
static object Locker = new object(); //anywhere in the class
//then in your method
if (inputword.Length != 0)
{
bool correct;
bool isEnglish;
lock(Locker) {isEnglish = IsEnglish(inputword);}
if(isEnglish)
{
//..do your stuff
}
//rest of your function
}

Categories