LockRecursionException in multiple threaded function - c#

I dont know how to describe it but I'm getting exception that schouldn't have a place when a code is good written. This exception is about issue with ReaderWriterLockSlim and it's LockRecursionException; it is appearing at "ScreenLocker.EnterReadLock();" line. Can't find problem with my code and description what to do or what might be wrong at internet, that's why i writting this question here and asking you all for help. This is code I have problem with:
public static List<Dictionary<int, int>> RunTasks(ScreenScanning ss)
{
var listOfTasks = new List<Task>();
List<Dictionary<int, int>> PosXOfBlocksAndMeaningOfIt = new List<Dictionary<int, int>>();
for (var i = 0; i <= BlocksOnYAxisOnScreen; i++)
{
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});
listOfTasks.Add(t);
}
Task.WaitAll(listOfTasks.ToArray());
return PosXOfBlocksAndMeaningOfIt;
}
and that are functions called by this method:
public Dictionary<int, int> XAxysScan(int posY, Bitmap screen, ref ReaderWriterLockSlim screenLocker)
{
screenLocker.ExitReadLock();
Dictionary<int, int> partOfMainTable = new Dictionary<int, int>();
partOfMainTable.Add(666, posY); //used in BotViewUpdate in DataToTableInterpreter
for (int i = 0; i <= 1920; i++)
{
if (screen.GetPixel(i, posY) == ColorsInRow[0])
{
if (IsFarmable(posY, ColorsInRow, i, screen))
{
partOfMainTable.Add(i, 1);
}
}
else if (IsBackground(BackgroundColors, i, posY, screen))
{
partOfMainTable.Add(i, 0);
}
else
{
partOfMainTable.Add(i, 2);
}
}
return partOfMainTable;
}
How can you see I'm releaseing lock right after entering XAxysScan function.

How can you see I'm releasing lock right after entering XAxysScan function.
The ReaderWriterLockSlim is a synchronization object that allows multiple threads to read from a resource, but only allow 1 resource to write to it(ideally).
The reason why this is important is because the specific way that ReaderWriterLockSlim is implemented to achieve this effect, requires something called Managed Thread Affinity, which basically means that whatever Task or Thread that called the EnterReadLock() must be the same Task or thread that calls ExitReadLock();.
When we look at the following, we can see you Have RunTasks(ScreenScanning ss) enter the lock, but you immediately start a new child Task and pass the ReaderWriterLockSlim as a reference to XAxysScan().
ScreenLocker.EnterReadLock();
var t = new Task(() =>
{
PosXOfBlocksAndMeaningOfIt.Add(ss.XAxysScan(PosYOfRowsToScan[i], Screen, ref ScreenLocker));
});
Only the same Task that enters a lock can be the one to release that lock. At least for synchronization objects like ReaderWriterLockSlim that use Managed Thread Affinity.
Consider moving EnterReadLock() into the XAxysScan() method.
public Dictionary<int, int> XAxysScan(int posY, Bitmap screen, ref ReaderWriterLockSlim screenLocker)
{
screenLocker.EnterReadLock();
try{
Dictionary<int, int> partOfMainTable = new Dictionary<int, int>();
partOfMainTable.Add(666, posY); //used in BotViewUpdate in DataToTableInterpreter
for (int i = 0; i <= 1920; i++)
{
if (screen.GetPixel(i, posY) == ColorsInRow[0])
{
if (IsFarmable(posY, ColorsInRow, i, screen))
{
partOfMainTable.Add(i, 1);
}
}
else if (IsBackground(BackgroundColors, i, posY, screen))
{
partOfMainTable.Add(i, 0);
}
else
{
partOfMainTable.Add(i, 2);
}
}
return partOfMainTable;
}
finally
{
// make sure that even if we encounter an error, we still exit the lock so other threads can enter the lock / begin writing
screenLocker.ExitReadLock();
}

Related

Thread-safe buffer that propagates the latest data

I have a data source which creates (produces) a PointF every 15 to 20 milliseconds.
I need to store (consume) such points every 10ms. My approach is to use a 3 points wide buffer and pointers to achieve a lock-free access:
protected class PosBuffer
{
PointF[] m_Buffer = new PointF[3];
volatile int m_ReadPointer = 0;
volatile int m_WritePointer = 1;
internal PosBuffer()
{
m_Buffer[0] = new PointF(0, 0);
m_Buffer[1] = new PointF(0, 0);
m_Buffer[2] = new PointF(0, 0);
}
internal void Write(PointF point)
{
m_Buffer[m_WritePointer] = point;
m_ReadPointer++;
if (m_ReadPointer == 3) m_ReadPointer = 0;
m_WritePointer++;
if (m_WritePointer == 3) m_WritePointer = 0;
}
internal PointF Read()
{
return m_Buffer[m_ReadPointer];
}
}
My idea is that
as soon as new data arrives it will be stored 'above' the old data. Then the read pointer is set to this position and then the write pointer is incremented.
in case now new data has been produced the consumer thread reads the old data again.
This construction allows different or inconstant read and write rates.
My questions are:
would this approach work?
Do I need locks/monitors/critical sections...
Would I need to disable optimization?
Are there known better solutions?
Try running this code:
async Task Main()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
var pb = new PosBuffer();
var tw = Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
break;
pb.Write(new PointF());
}
});
var tr = Task.Run(() =>
{
while (true)
{
if (ct.IsCancellationRequested)
break;
pb.Read();
}
});
await Task.Delay(TimeSpan.FromSeconds(5.0));
cts.Cancel();
}
Fairly quickly it throws IndexOutOfRangeException. You're letting the value of the "pointers" (bad name by the way) be 3 before dropping back to zero and in the time it takes to change it down the read operation throws.
The problem goes away if you increment like this:
m_ReadPointer = (m_ReadPointer == m_Buffer.Length - 1) ? 0 : m_ReadPointer + 1;
m_WritePointer = (m_WritePointer == m_Buffer.Length - 1) ? 0 : m_WritePointer + 1;
Now, if you have multiple writers then you're definitely going to need locking.
You could consider using a BroadcastBlock<T> from the TPL Dataflow library:
Provides a buffer for storing at most one element at time, overwriting each message with the next as it arrives.
using System.Threading.Tasks.Dataflow;
// Initialize
BroadcastBlock<PointF> block = new(x => x);
// Write the latest value
block.Post(new PointF(0, 0));
// Fetch the latest value
PointF point = await block.ReceiveAsync();
Another idea is to use a BehaviorSubject<T> from the Rx library.
Represents a value that changes over time.
using System.Reactive.Subjects;
// Initialize
BehaviorSubject<PointF> subject = new(new PointF(0, 0));
// Write the latest value
subject.OnNext(new PointF(0, 0));
// Get the latest value
PointF point = subject.Value;
Both classes (BroadcastBlock<T> and BehaviorSubject<T>) are thread-safe.
For your case of single value, I would suggest ReaderWriterLockSlim assuming
you need thread safe reads and writes with multiple threads.
protected class PosBuffer
{
private PointF m_Buffer;
private ReaderWriterLockSlim m_Lock = new();
internal void Write(PointF point)
{
m_Lock.EnterWriteLock();
try
{
m_Buffer = point;
}
finally
{
m_Lock.ExitWriteLock();
}
}
internal PointF Read()
{
m_Lock.EnterReadLock();
try
{
return m_Buffer;
}
finally
{
m_Lock.ExitReadLock();
}
}
}

Use Task.Run instead of Delegate.BeginInvoke

I have recently upgraded my projects to ASP.NET 4.5 and I have been waiting a long time to use 4.5's asynchronous capabilities. After reading the documentation I'm not sure whether I can improve my code at all.
I want to execute a task asynchronously and then forget about it. The way that I'm currently doing this is by creating delegates and then using BeginInvoke.
Here's one of the filters in my project with creates an audit in our database every time a user accesses a resource that must be audited:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var id = WebSecurity.CurrentUserId;
var invoker = new MethodInvoker(delegate
{
var audit = new Audit
{
Id = Guid.NewGuid(),
IPAddress = request.UserHostAddress,
UserId = id,
Resource = request.RawUrl,
Timestamp = DateTime.UtcNow
};
var database = (new NinjectBinder()).Kernel.Get<IDatabaseWorker>();
database.Audits.InsertOrUpdate(audit);
database.Save();
});
invoker.BeginInvoke(StopAsynchronousMethod, invoker);
base.OnActionExecuting(filterContext);
}
But in order to finish this asynchronous task, I need to always define a callback, which looks like this:
public void StopAsynchronousMethod(IAsyncResult result)
{
var state = (MethodInvoker)result.AsyncState;
try
{
state.EndInvoke(result);
}
catch (Exception e)
{
var username = WebSecurity.CurrentUserName;
Debugging.DispatchExceptionEmail(e, username);
}
}
I would rather not use the callback at all due to the fact that I do not need a result from the task that I am invoking asynchronously.
How can I improve this code with Task.Run() (or async and await)?
If I understood your requirements correctly, you want to kick off a task and then forget about it. When the task completes, and if an exception occurred, you want to log it.
I'd use Task.Run to create a task, followed by ContinueWith to attach a continuation task. This continuation task will log any exception that was thrown from the parent task. Also, use TaskContinuationOptions.OnlyOnFaulted to make sure the continuation only runs if an exception occurred.
Task.Run(() => {
var audit = new Audit
{
Id = Guid.NewGuid(),
IPAddress = request.UserHostAddress,
UserId = id,
Resource = request.RawUrl,
Timestamp = DateTime.UtcNow
};
var database = (new NinjectBinder()).Kernel.Get<IDatabaseWorker>();
database.Audits.InsertOrUpdate(audit);
database.Save();
}).ContinueWith(task => {
task.Exception.Handle(ex => {
var username = WebSecurity.CurrentUserName;
Debugging.DispatchExceptionEmail(ex, username);
});
}, TaskContinuationOptions.OnlyOnFaulted);
As a side-note, background tasks and fire-and-forget scenarios in ASP.NET are highly discouraged. See The Dangers of Implementing Recurring Background Tasks In ASP.NET
It may sound a bit out of scope, but if you just want to forget after you launch it, why not using directly ThreadPool?
Something like:
ThreadPool.QueueUserWorkItem(
x =>
{
try
{
// Do something
...
}
catch (Exception e)
{
// Log something
...
}
});
I had to do some performance benchmarking for different async call methods and I found that (not surprisingly) ThreadPool works much better, but also that, actually, BeginInvoke is not that bad (I am on .NET 4.5). That's what I found out with the code at the end of the post. I did not find something like this online, so I took the time to check it myself. Each call is not exactly equal, but it is more or less functionally equivalent in terms of what it does:
ThreadPool: 70.80ms
Task: 90.88ms
BeginInvoke: 121.88ms
Thread: 4657.52ms
public class Program
{
public delegate void ThisDoesSomething();
// Perform a very simple operation to see the overhead of
// different async calls types.
public static void Main(string[] args)
{
const int repetitions = 25;
const int calls = 1000;
var results = new List<Tuple<string, double>>();
Console.WriteLine(
"{0} parallel calls, {1} repetitions for better statistics\n",
calls,
repetitions);
// Threads
Console.Write("Running Threads");
results.Add(new Tuple<string, double>("Threads", RunOnThreads(repetitions, calls)));
Console.WriteLine();
// BeginInvoke
Console.Write("Running BeginInvoke");
results.Add(new Tuple<string, double>("BeginInvoke", RunOnBeginInvoke(repetitions, calls)));
Console.WriteLine();
// Tasks
Console.Write("Running Tasks");
results.Add(new Tuple<string, double>("Tasks", RunOnTasks(repetitions, calls)));
Console.WriteLine();
// Thread Pool
Console.Write("Running Thread pool");
results.Add(new Tuple<string, double>("ThreadPool", RunOnThreadPool(repetitions, calls)));
Console.WriteLine();
Console.WriteLine();
// Show results
results = results.OrderBy(rs => rs.Item2).ToList();
foreach (var result in results)
{
Console.WriteLine(
"{0}: Done in {1}ms avg",
result.Item1,
(result.Item2 / repetitions).ToString("0.00"));
}
Console.WriteLine("Press a key to exit");
Console.ReadKey();
}
/// <summary>
/// The do stuff.
/// </summary>
public static void DoStuff()
{
Console.Write("*");
}
public static double RunOnThreads(int repetitions, int calls)
{
var totalMs = 0.0;
for (var j = 0; j < repetitions; j++)
{
Console.Write(".");
var toProcess = calls;
var stopwatch = new Stopwatch();
var resetEvent = new ManualResetEvent(false);
var threadList = new List<Thread>();
for (var i = 0; i < calls; i++)
{
threadList.Add(new Thread(() =>
{
// Do something
DoStuff();
// Safely decrement the counter
if (Interlocked.Decrement(ref toProcess) == 0)
{
resetEvent.Set();
}
}));
}
stopwatch.Start();
foreach (var thread in threadList)
{
thread.Start();
}
resetEvent.WaitOne();
stopwatch.Stop();
totalMs += stopwatch.ElapsedMilliseconds;
}
return totalMs;
}
public static double RunOnThreadPool(int repetitions, int calls)
{
var totalMs = 0.0;
for (var j = 0; j < repetitions; j++)
{
Console.Write(".");
var toProcess = calls;
var resetEvent = new ManualResetEvent(false);
var stopwatch = new Stopwatch();
var list = new List<int>();
for (var i = 0; i < calls; i++)
{
list.Add(i);
}
stopwatch.Start();
for (var i = 0; i < calls; i++)
{
ThreadPool.QueueUserWorkItem(
x =>
{
// Do something
DoStuff();
// Safely decrement the counter
if (Interlocked.Decrement(ref toProcess) == 0)
{
resetEvent.Set();
}
},
list[i]);
}
resetEvent.WaitOne();
stopwatch.Stop();
totalMs += stopwatch.ElapsedMilliseconds;
}
return totalMs;
}
public static double RunOnBeginInvoke(int repetitions, int calls)
{
var totalMs = 0.0;
for (var j = 0; j < repetitions; j++)
{
Console.Write(".");
var beginInvokeStopwatch = new Stopwatch();
var delegateList = new List<ThisDoesSomething>();
var resultsList = new List<IAsyncResult>();
for (var i = 0; i < calls; i++)
{
delegateList.Add(DoStuff);
}
beginInvokeStopwatch.Start();
foreach (var delegateToCall in delegateList)
{
resultsList.Add(delegateToCall.BeginInvoke(null, null));
}
// We lose a bit of accuracy, but if the loop is big enough,
// it should not really matter
while (resultsList.Any(rs => !rs.IsCompleted))
{
Thread.Sleep(10);
}
beginInvokeStopwatch.Stop();
totalMs += beginInvokeStopwatch.ElapsedMilliseconds;
}
return totalMs;
}
public static double RunOnTasks(int repetitions, int calls)
{
var totalMs = 0.0;
for (var j = 0; j < repetitions; j++)
{
Console.Write(".");
var resultsList = new List<Task>();
var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i < calls; i++)
{
resultsList.Add(Task.Factory.StartNew(DoStuff));
}
// We lose a bit of accuracy, but if the loop is big enough,
// it should not really matter
while (resultsList.Any(task => !task.IsCompleted))
{
Thread.Sleep(10);
}
stopwatch.Stop();
totalMs += stopwatch.ElapsedMilliseconds;
}
return totalMs;
}
}
Here's one of the filters in my project with creates an audit in our database every time a user accesses a resource that must be audited
Auditing is certainly not something I would call "fire and forget". Remember, on ASP.NET, "fire and forget" means "I don't care whether this code actually executes or not". So, if your desired semantics are that audits may occasionally be missing, then (and only then) you can use fire and forget for your audits.
If you want to ensure your audits are all correct, then either wait for the audit save to complete before sending the response, or queue the audit information to reliable storage (e.g., Azure queue or MSMQ) and have an independent backend (e.g., Azure worker role or Win32 service) process the audits in that queue.
But if you want to live dangerously (accepting that occasionally audits may be missing), you can mitigate the problems by registering the work with the ASP.NET runtime. Using the BackgroundTaskManager from my blog:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var id = WebSecurity.CurrentUserId;
BackgroundTaskManager.Run(() =>
{
try
{
var audit = new Audit
{
Id = Guid.NewGuid(),
IPAddress = request.UserHostAddress,
UserId = id,
Resource = request.RawUrl,
Timestamp = DateTime.UtcNow
};
var database = (new NinjectBinder()).Kernel.Get<IDatabaseWorker>();
database.Audits.InsertOrUpdate(audit);
database.Save();
}
catch (Exception e)
{
var username = WebSecurity.CurrentUserName;
Debugging.DispatchExceptionEmail(e, username);
}
});
base.OnActionExecuting(filterContext);
}

What is the best scenario for one fast producer multiple slow consumers?

I'm looking for the best scenario to implement one producer multiple consumer multithreaded application.
Currently I'm using one queue for shared buffer but it's much slower than the case of one producer one consumer.
I'm planning to do it like this:
Queue<item>[] buffs = new Queue<item>[N];
object[] _locks = new object[N];
static void Produce()
{
int curIndex = 0;
while(true)
{
// Produce item;
lock(_locks[curIndex])
{
buffs[curIndex].Enqueue(curItem);
Monitor.Pulse(_locks[curIndex]);
}
curIndex = (curIndex+1)%N;
}
}
static void Consume(int myIndex)
{
item curItem;
while(true)
{
lock(_locks[myIndex])
{
while(buffs[myIndex].Count == 0)
Monitor.Wait(_locks[myIndex]);
curItem = buffs[myIndex].Dequeue();
}
// Consume item;
}
}
static void main()
{
int N = 100;
Thread[] consumers = new Thread[N];
for(int i = 0; i < N; i++)
{
consumers[i] = new Thread(Consume);
consumers[i].Start(i);
}
Thread producer = new Thread(Produce);
producer.Start();
}
Use a BlockingCollection
BlockingCollection<item> _buffer = new BlockingCollection<item>();
static void Produce()
{
while(true)
{
// Produce item;
_buffer.Add(curItem);
}
// eventually stop producing
_buffer.CompleteAdding();
}
static void Consume(int myIndex)
{
foreach (var curItem in _buffer.GetConsumingEnumerable())
{
// Consume item;
}
}
static void main()
{
int N = 100;
Thread[] consumers = new Thread[N];
for(int i = 0; i < N; i++)
{
consumers[i] = new Thread(Consume);
consumers[i].Start(i);
}
Thread producer = new Thread(Produce);
producer.Start();
}
If you don't want to specify number of threads from start you can use Parallel.ForEach instead.
static void Consume(item curItem)
{
// consume item
}
void Main()
{
Thread producer = new Thread(Produce);
producer.Start();
Parallel.ForEach(_buffer.GetConsumingPartitioner(), Consumer)
}
Using more threads won't help. It may even reduce performance. I suggest you try to use ThreadPool where every work item is one item created by the producer. However, that doesn't guarantee the produced items to be consumed in the order they were produced.
Another way could be to reduce the number of consumers to 4, for example and modify the way they work as follows:
The producer adds the new work to the queue. There's only one global queue for all worker threads. It then sets a flag to indicate there is new work like this:
ManualResetEvent workPresent = new ManualResetEvent(false);
Queue<item> workQueue = new Queue<item>();
static void Produce()
{
while(true)
{
// Produce item;
lock(workQueue)
{
workQueue.Enqueue(newItem);
workPresent.Set();
}
}
}
The consumers wait for work to be added to the queue. Only one consumer will get to do its job. It then takes all the work from the queue and resets the flag. The producer will not be able to add new work until that is done.
static void Consume()
{
while(true)
{
if (WaitHandle.WaitOne(workPresent))
{
workPresent.Reset();
Queue<item> localWorkQueue = new Queue<item>();
lock(workQueue)
{
while (workQueue.Count > 0)
localWorkQueue.Enqueue(workQueue.Dequeue());
}
// Handle items in local work queue
...
}
}
}
That outcome of this, however, is a bit unpredictable. It could be that one thread is doing all the work and the others do nothing.
I don't see why you have to use multiple queues. Just reduce the amount of locking. Here is an sample where you can have a large number of consumers and they all wait for new work.
public class MyWorkGenerator
{
ConcurrentQueue<object> _queuedItems = new ConcurrentQueue<object>();
private object _lock = new object();
public void Produce()
{
while (true)
{
_queuedItems.Enqueue(new object());
Monitor.Pulse(_lock);
}
}
public object Consume(TimeSpan maxWaitTime)
{
if (!Monitor.Wait(_lock, maxWaitTime))
return null;
object workItem;
if (_queuedItems.TryDequeue(out workItem))
{
return workItem;
}
return null;
}
}
Do note that Pulse() will only trigger one consumer at a time.
Example usage:
static void main()
{
var generator = new MyWorkGenerator();
var consumers = new Thread[20];
for (int i = 0; i < consumers.Length; i++)
{
consumers[i] = new Thread(DoWork);
consumers[i].Start(generator);
}
generator.Produce();
}
public static void DoWork(object state)
{
var generator = (MyWorkGenerator) state;
var workItem = generator.Consume(TimeSpan.FromHours(1));
while (workItem != null)
{
// do work
workItem = generator.Consume(TimeSpan.FromHours(1));
}
}
Note that the actual queue is hidden in the producer as it's imho an implementation detail. The consumers doesn't really have to know how the work items are generated.

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
}

Thread lock clarification

I am a beginer in programing.
When i execute the code by locking the operation:
class ThreadSafe
{
static List<string> list = new List<string>();
static object obj=new object();
static void Main()
{
new Thread(AddItems).Start();
new Thread(AddItems).Start();
foreach (string str in list)
{
Console.WriteLine(str);
}
Console.WriteLine("Count=" + list.Count.ToString());
Console.ReadKey(true);
}
static void AddItems()
{
lock (obj)
{
for (int i = 1; i < 10; i++)
list.Add("Item " + i.ToString());
}
}
}
even i am reciving,"InvalidOperationException".What would be the code alteration?
The issue is that your threads are altering the list while it is trying to be read.
class ThreadSafe
{
static List<string> list = new List<string>();
static object obj=new object();
static void Main()
{
var t1 = new Thread(AddItems);
var t2 = new Thread(AddItems);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
foreach (string str in list)
{
Console.WriteLine(str);
}
Console.WriteLine("Count=" + list.Count.ToString());
Console.ReadKey(true);
}
static void AddItems()
{
for (int i = 1; i < 10; i++)
lock (obj)
{
list.Add("Item " + i.ToString());
}
}
}
The difference being that this code waits for both threads to complete before showing the results.
I also moved the lock around the specific instruction that needs to be locked, so that both threads can run concurrently.
You're enumerating over a collection with foreach (string str in list) while modifying it in AddItems(). For this code to work property you'll either have to Thread.Join() both threads (so that both will finish adding items to a list; I'm not sure, however if Add is threadsafe; I bet it's not, so you'll have to account for that by locking on SyncRoot) or use a ReaderWriterLock to logically separate these operations.
You are looping through the result list before the two AddItems threads have finished populating the list. So, the foreach complains that the list was updated while it was looping through that list.
Something like this should help:
System.Threading.Thread.Sleep(0); // Let the other threads get started on the list.
lock(obj)
{
foreach (string str in list)
{
Console.WriteLine(str);
}
}
Watch out though! This doesn't guarantee that the second thread will finish it's job before you have read through the list provided by the first thread (assuming the first thread grabs the lock first).
You will need some other mechanism (like John Gietzen's solution) to know when both threads have finished, before reading the results.
Use the debugger. :)
You receive the InvalidOperationException on the foreach.
What happens, is that the foreach is executed while your threads are still running.
Therefore, you're iterating over your list, while items are being added to the list. So, the contents of the list are changing, and therefore, the foreach throws an exception.
You can avoid this problem by calling 'Join'.
static void Main()
{
Thread t1 = new Thread (AddItems);
Thread t2 =new Thread (AddItems);
t1.Start ();
t2.Start ();
t1.Join ();
t2.Join ();
foreach( string str in list )
{
Console.WriteLine (str);
}
Console.WriteLine ("Count=" + list.Count.ToString ());
Console.ReadKey (true);
}
I have changed the code, which proves lock does nothing.
I expected add2 won't show up until add1 has finished. But add1 and add2 just mingled together.
using System;
using System.Threading;
public static class Example
{
public static void Main()
{
int data= 0;
Thread t1 = new Thread(()=> add1(ref data));
Thread t2 = new Thread(() => add2(ref data));
t1.Start();
t2.Start();
}
static void add1(ref int x)
{
object lockthis = new object();
lock (lockthis)
{
for (int i = 0; i < 30; i++)
{
x += 1;
Console.WriteLine("add1 " + x);
}
}
}
static void add2(ref int x)
{
object lockthis = new object();
lock (lockthis)
{
for (int i = 0; i < 30; i++)
{
x += 3;
Console.WriteLine("add2 " + x);
}
}
}
}

Categories