Reactive Extensions Unsubscribe issue - c#

I am trying to dynamically add and remove subscribers to an Observable BlockingCollection using the ThreadPoolScheduler.
I am not sure if this is a problem with my code or in RX itself, but I had assumed in this case I should be able to subscribe/unsubscribe as needed.
I have reduced the issue down to the test pasted below.
The code works correctly until I call Dispose on subscribers and then add new subscribers.
Essentially I seem to get old threads still de-queing the Observables but never doing anything with them.
Here is the unit test, it sets up 32 subscribers, adds 64 objects, then unsubscribes and repeats the same test. I have removed any additional code (and added some sleeps just for the test to make sure the threads are def done before I unsubscribe)
The first 64 are processed correctly, but the second set only 32 objects are passed to my subscriber.
[TestClass]
public class RxTests
{
public class ObservTest
{
public BlockingCollection<ObserverTests.UnitTestObservable> mBlockingCollection = new BlockingCollection<ObserverTests.UnitTestObservable>();
public IObservable<ObserverTests.UnitTestObservable> mObservableBlockingCollection;
private static readonly object ObservableLock = new object();
private static volatile ObservTest ObservableInstance;
public static ObservTest Instance
{
get
{
if (ObservableInstance != null)
return ObservableInstance;
lock (ObservableLock)
{
if (ObservableInstance == null)
{
ObservTest observable = new ObservTest();
observable.mObservableBlockingCollection = observable.mBlockingCollection.GetConsumingEnumerable().ToObservable(ThreadPoolScheduler.Instance);
ObservableInstance = observable;
}
return ObservableInstance;
}
}
}
private int count = 0;
public void Release()
{
Interlocked.Increment(ref count);
Console.WriteLine("Release {0} : {1}", count, Thread.CurrentThread.ManagedThreadId);
}
public void LogCount()
{
Console.WriteLine("Total :{0}", count);
}
}
[TestMethod]
public void TestMethod1()
{
IList<IDisposable> subscribers = new List<IDisposable>();
for (int count = 0; count < 32; count++)
{
IDisposable disposable = ObservTest.Instance.mObservableBlockingCollection.Subscribe(Observe);
subscribers.Add(disposable);
}
for (int count = 0; count < 64; count++)
{
ObserverTests.UnitTestObservable observable = new ObserverTests.UnitTestObservable
{
Name = string.Format("{0}", count)
};
ObservTest.Instance.mBlockingCollection.Add(observable);
}
Thread.Sleep(5000);
foreach (IDisposable disposable in subscribers)
{
disposable.Dispose();
}
subscribers.Clear();
for (int count = 0; count < 32; count++)
{
IDisposable disposable = ObservTest.Instance.mObservableBlockingCollection.Subscribe(Observe);
subscribers.Add(disposable);
}
for (int count = 0; count < 64; count++)
{
ObserverTests.UnitTestObservable observable = new ObserverTests.UnitTestObservable
{
Name = string.Format("{0}", count)
};
ObservTest.Instance.mBlockingCollection.Add(observable);
}
Thread.Sleep(3000);
ObservTest.Instance.LogCount();
}
public static void Observe(ObserverTests.UnitTestObservable observable)
{
Console.WriteLine("Observe {0} : {1}", observable.Name, Thread.CurrentThread.ManagedThreadId);
ObservTest.Instance.Release();
}
}
So the final count in the output is 96 when I would expect it to be 128
If I reduce the number of initial subscribers from 32 the processed count increases.
e.g. if I reduce the count from 32 to 16 in the first loop I get a count of 112.
if I reduce it to 8 I get 120
I am aiming for a system where as the number of tasks being executed is increased so too are the number of subscribers available to process them.

Related

Hitting Synchronization LockException when resizing concurrent dictionary

Would anyone know why I hit the SynchronizationLockException when attempting a resize operation?
Based on the documentation for this error, I understand this happens when the current thread doesn't own the lock but based on the TryResize function, I think this thread should be owning all the locks
To illustrate the bug, I deliberately kept the load factor equal to 0 so that after the very first add operation, the implementation will attempt a resize operation
Here is the test I ran:
public async Task ThreeThreadAdd()
{
MyConcurrentDictionary<int, int> dict = new MyConcurrentDictionary<int, int>();
var task1 = Task.Run(() => dict.TryAdd(1, 1));
var sameBucketAsTask1 = Task.Run(() => dict.TryAdd(11, 1));
var task2 = Task.Run(() => dict.TryAdd(2, 2));
await Task.WhenAll(task1, sameBucketAsTask1, task2);
Assert.AreEqual(3, dict.Count());
}
Here is the implementation:
namespace DictionaryImplementations
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
public class MyConcurrentDictionary<TKey, TValue>
{
internal class Entry<TKey, TValue>
{
internal TKey key;
internal TValue value;
}
internal class Table
{
internal readonly object[] locks;
internal readonly List<Entry<TKey, TValue>>[] buckets;
internal Table(object[] locks, List<Entry<TKey, TValue>>[] buckets)
{
this.locks = locks;
this.buckets = buckets;
}
}
private double loadFactor;
private int count;
private volatile int bucketSize;
private volatile Table table;
public MyConcurrentDictionary()
{
//// starting with size 2 to illustrate resize issue
int size = 2;
this.bucketSize = size;
object[] locks = new object[size];
for (int i = 0; i < locks.Length; i++)
{
locks[i] = new object();
}
List<Entry<TKey, TValue>>[] buckets = new List<Entry<TKey, TValue>>[size];
for (int i = 0; i < buckets.Length; i++)
{
buckets[i] = new List<Entry<TKey, TValue>>();
}
Table table = new Table(locks, buckets);
this.table = table;
this.loadFactor = 0;
}
private void TryAcquireLocks(int inclusiveStart, int exclusiveEnd)
{
for (int i = inclusiveStart; i < exclusiveEnd; i++)
{
while (!Monitor.TryEnter(this.table.locks[i], 100))
{
continue;
}
}
}
private void ReleaseLocks(int inclusiveStart, int exclusiveEnd)
{
for (int i = inclusiveStart; i < exclusiveEnd; i++)
{
Monitor.Exit(this.table.locks[i]);
}
}
/// <returns>true if the k/v pair was added, false if key already exists</returns>
public bool TryAdd(TKey key, TValue value)
{
int hashCode = key.GetHashCode();
// is the volatile read safe?
int index = hashCode % this.bucketSize;
// acquire the lock
this.TryAcquireLocks(index, index + 1);
try
{
foreach (var entry in this.table.buckets[index])
{
if (entry.key.Equals(key))
{
return false;
}
}
Entry<TKey, TValue> newEntry = new Entry<TKey, TValue>()
{
key = key,
value = value
};
this.table.buckets[index].Add(newEntry);
Interlocked.Increment(ref this.count);
return true;
}
finally
{
this.ReleaseLocks(index, index + 1);
// attempt resize operation
this.TryResize();
}
}
public bool TryRemove(TKey key, out TValue oldValue)
{
oldValue = default(TValue);
int hashCode = key.GetHashCode();
// is this volatile read safe?
int index = hashCode % this.bucketSize;
// acquire the lock
this.TryAcquireLocks(index, index + 1);
try
{
bool found = false;
int entryIndex = 0;
foreach (var entry in this.table.buckets[index])
{
if (!entry.key.Equals(key))
{
entryIndex++;
}
else
{
found = true;
break;
}
}
if (!found)
{
return false;
}
this.table.buckets[index].RemoveAt(entryIndex);
// `volatile` doesn't work in this hashmap model since we have locks for each bucket
// since increment isn't an atomic operation, using `volatile` alone will not help
Interlocked.Decrement(ref this.count);
return true;
}
finally
{
this.ReleaseLocks(index, index + 1);
}
}
public int Count()
{
// `Interlock` should flush all caches so that we observe latest value
return this.count;
}
public bool ContainsKey(TKey key)
{
int hashCode = key.GetHashCode();
int index = hashCode % this.bucketSize;
// acquire the lock
// in this case, we need to take a lock to guard against collection being modified
this.TryAcquireLocks(index, index + 1);
try
{
List<Entry<TKey, TValue>> bucket = this.table.buckets[index];
return bucket.Any(item => item.key.Equals(key));
}
finally
{
this.ReleaseLocks(index, index + 1);
}
}
private void TryResize()
{
double currentLoad = (this.count * (1.0)) / this.bucketSize;
if (currentLoad < this.loadFactor)
{
return;
}
// locks are re-entrant for the same thread. So, we should not deadlock when acquiring same lock
this.TryAcquireLocks(0, this.bucketSize);
// store a reference to the locks array before the resize
var prevLockReference = this.table.locks;
try
{
int newBucketSize = this.bucketSize * 2;
object[] newLocks = new object[newBucketSize];
Array.Copy(this.table.locks, newLocks, this.table.locks.Length);
for (int i = this.table.locks.Length; i < newBucketSize; i++)
{
newLocks[i] = new object();
}
List<Entry<TKey, TValue>>[] newBuckets = new List<Entry<TKey, TValue>>[newBucketSize];
for (int i = 0; i < newBuckets.Length; i++)
{
newBuckets[i] = new List<Entry<TKey, TValue>>();
}
// re-compute distribution
foreach (List<Entry<TKey, TValue>> bucket in this.table.buckets)
{
foreach (Entry<TKey, TValue> entry in bucket)
{
int hashCode = entry.key.GetHashCode();
int newIndex = hashCode % newBucketSize;
newBuckets[newIndex].Add(new Entry<TKey, TValue>() { key = entry.key, value = entry.value });
}
}
Table newTable = new Table(newLocks, newBuckets);
// perform new assignments
// volatile reads will flush the cache
this.bucketSize = newBucketSize;
this.table = newTable;
}
finally
{
for (int i = 0; i < prevLockReference.Length; i++)
{
Monitor.Exit(prevLockReference[i]);
}
}
}
}
}
That's a nice brain teaser. I will leave all the constructive criticism about your code for the end.
The bug hunt
The bug is in TryResize. A thread comes in trying to resize when the bucket size is, say, 2. It gets to the entry of your critical section:
// locks are re-entrant for the same thread. So, we should not deadlock when acquiring same lock
this.TryAcquireLocks(0, this.bucketSize);
It acquires both locks and goes on its merry way to resize the dictionary. The logic is all fine, you copy the locks to a new array, reallocate buckets, re-compute the distribution...
Then a Spoiler thread comes along and also TryResizes. It gets to the critical section, invokes TryAcquireLocks(0, 2), tries to acquire the first lock and hangs.
In the meantime the first thread finishes recalculation, assigns this.bucketSize = 4, reassigns the internal table along with its locks and enters finally to release both locks.
Now the Spoiler thread wakes up, because it can now acquire lock number 0. It loops again, looks at the new table since it's correctly volatile and acquires lock number 1. But here's the kicker -- the Spoiler thread never witnessed the this.bucketSize reassignment. It is not aware that there are twice as many locks to acquire now, since it's executing TryAcquireLocks(0, 2). So it only acquires the 2 first locks in the table!
And that's it, not only is the critical section's precondition violated, when this Spoiler thread executes finally it will try to release all 4 locks, since the loop there explicitly goes up to the Length of the lock table. But it doesn't own the new 2 locks, only the original first 2, so you get a SynchronizationLockException.
An immediate fix is to introduce a new method that will always acquire all locks, even if their count increases between calls:
private void TryAcquireAllLocks()
{
for (int i = 0; i < this.bucketSize; i++)
{
while (!Monitor.TryEnter(this.table.locks[i], 100))
{
continue;
}
}
}
And then replace the bugged line in TryResize with
this.TryAcquireAllLocks();
The "please don't put this on production" section, a.k.a. just use ConcurrentDictionary
One of the reasons why you should probably never implement such complicated structures by yourself is this bug you just got. It's very non-trivial to track it down, and it takes a lot of reading to understand all the code that you've written and convince someone it's correct.
Your code already contains a lot of bugs waiting to happen. You're prone to the same error as old versions of Monitor.Enter, where your thread can die while you're holding a lock and deadlock the application -- what happens, when a thread acquired part of the locks that it needs to perform some operation and then dies? It'll never release them, and no one will ever get to use the dictionary again! I also don't get why you're passing in a timeout to Monitor.Enter if you always try to reacquire the lock right after.
If you're writing this code as an exercise, great, do so, test it, and then post it to Code Review StackExchange to get some quality feedback. Actually sounds like a great exercise.
But please, for the sake of us all, don't use your own implementation in production. The BCL version is well audited by experts whose only job is to make sure their standard implementations work, there's no way your custom code is going to be more robust than theirs.

SynchronizedCollection InvalidOperationException/System.ArgumentException

I wrote some classes to test multithreading using SynchronizedCollection.
class MultithreadTesting
{
public readonly SynchronizedCollection<int> testlist = new SynchronizedCollection<int>();
public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty
{
get
{
return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);
}
}
public void Test()
{
int numthreads = 20;
Thread[] threads = new Thread[numthreads];
List<Task> taskList = new List<Task>();
for (int i = 0; i < numthreads / 2; i++)
{
taskList.Add(Task.Factory.StartNew(() =>
{
for (int j = 0; j < 100000; j++)
{
testlist.Add(42);
}
}));
}
for (int i = numthreads / 2; i < numthreads; i++)
{
taskList.Add(Task.Factory.StartNew(() =>
{
var sum = 0;
foreach (int num in pubReadOnlyProperty)
{
sum += num;
}
}));
}
Task.WaitAll(taskList.ToArray());
testlist.Clear();
}
}
to run it I use
MultithreadTesting test = new MultithreadTesting();
while (true)
test.Test();
But the code throws me System.ArgumentException: 'Destination array was not long enough. Check destIndex and length, and the array's lower bounds.'
If I try to use testlist in foreach, I get
System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'
However, MSDN tells us
SynchronizedReadOnlyCollection Class
Provides a thread-safe, read-only collection that contains objects of
a type specified by the generic parameter as elements.
The root cause of the error is that List<T> construction is not thread-safe.
Let's see what happens when constructing new SynchronizedReadOnlyCollection. Exception occurs in following line:
return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);
As exception StackTrace tells us, there is List<T>..ctor involved in construction process:
at System.Collections.Generic.SynchronizedCollection`1.CopyTo(T[] array, Int32 index)
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Collections.Generic.SynchronizedReadOnlyCollection`1..ctor(Object syncRoot, IEnumerable`1 list)
Following snippet from List<T> constructor shows where error happens. Code is copied from MS reference source, I cleaned unnecessary parts of code for easier reading. Please notice that between comments (1) and (2) there are other threads manipulating collection:
public List(IEnumerable<T> collection) {
ICollection<T> c = collection as ICollection<T>;
// (1) count is now current Count of collection
int count = c.Count;
// other threads can modify collection meanwhile
if (count == 0)
{
_items = _emptyArray;
}
else {
_items = new T[count];
// (2) SynchronizedCollection.CopyTo is called (which itself is thread-safe)
// Collection can still be modified between (1) and (2)
// No when _items.Count != c.Count -> Exception is raised.
c.CopyTo(_items, 0);
_size = count;
}
}
Solution
The problem can easily be fixed with locking testlist modification while constructing new SynchronizedReadOnlyCollection.
public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty
{
get
{
lock (testlist.SyncRoot)
{
return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);
}
}
}

c# 2 events with same nameļ¼Œ got chaos at running time, how should i avoid this?

I have a method "Add2List", who creates a ManualResetEvent and stores it in SortedList of a instance, then waits for signaling, then do some work and dispose the event.
I have another method "DoSomething", who listens to remote server and then signals the stored manual events according to Guid.
in the multithreading context, multi threads calls method "Add2List", so in the sortedlist there may have several manual event with same name at the same moment. But this may cause chaos. How should i avoid this?
To be simpler, i wrote this test code:
Class Program
{
static void Main(string[] args)
{
StringBuilder str = new StringBuilder();//a string to record what happened
//test iteratively
for(int i=0;i<100;i++)
{
EventHolder holder = new EventHolder();
Signaler ob2 = new Signaler();
Thread th1 = new Thread(holder.Add2List);
Thread th2 = new Thread(holder.Add2List);
Thread th3 = new Thread(ob2.DoSomething);
th1.Start(1);
th2.Start(2);
th3.Start();
//Make sure all thread is ended before the next iteration.
while(th1.IsAlive){ Thread.Sleep(200); }
while(th2.IsAlive){ Thread.Sleep(200); }
while(th3.IsAlive){ Thread.Sleep(200); }
}
Console.Read();
}
public class EventHolder
{
static SortedList<int, ManualResetEvent> MyManualEventList = new SortedList<int, ManualResetEvent>();
public EventHolder()
{
MyManualEventList = new SortedList<int, ManualResetEvent>();
Signaler.SignalMyManualEvent += OnSignalMyManualEvent;
}
void OnSignalMyManualEvent(int listindex)
{
try { MyManualEventList[listindex].Set(); }
catch(Exception e)
{
Console.WriteLine("Exception throws at " + System.DateTime.Now.ToString() +" Exception Message:"
Console.WriteLine(e.Message);
int temp = 0; //*Here is a breakpoint! To watch local variables when exception happens.
}
}
public void Add2List(object listindex)
{
ManualResetEvent MyManualEvent = new ManualResetEvent(false);
MyManualEvent.Reset();
MyManualEventList.Add((int)listindex, eve);
//in this test, this countdownevent need to be signaled twice, for it has to wait until all 2 event been added to MyManualEventList
Signaler.StartTrySignal.Signal();
MyManualEvent.WaitOne();
Console.WriteLine("Event" + ((int)listindex).ToString() + " been detected at " + System.DateTime.Now.Tostring());
MyManualEvent.Dispose();
}
}
public class Signaler
{
public delegate void Signalhandler(int listindex);
public static event Signalhandler SignalMyManualEvent;
public static CountDownEvent StartTrySignal = new CountDownEvent(2); // signaled twice so that the 2 manual events were added to sortedlist
void RaiseSignalMyManualEvent(int listindex)
{
var vr = SignalMyManualEvent;
if(vr != null)
vr(listindex);
}
int i = 0, j = 0, k = 0;
// here i use 2 prime numbers to simulate the happening of 2 random events
public Signaler()
{
StartTrySignal.Reset();
}
public void DoSomething()
{
StartTrySignal.Wait(); // wait for the 2 manual events been added to sortedlist
//To signal MyManualEventList[1] or MyManualEventList[2]
while(i + j == 0)
{
Random rnd = new Random();
k = rnd.Next();
if(k % 613 == 0) { i = 1; Console.WriteLine("Event1 Raised!"; RaiseSignalMyManualEvent(1); }
else if(k % 617 == 0) { j = 1; Console.WriteLine("Event1 Raised!"; RaiseSignalMyManualEvent(2); }
}
//if MyManualEventList[1] has not been signaled, wait something to happen, and signal it.
while(i == 0)
{
Random rnd = new Random();
k = rnd.Next();
if(k % 613 == 0)
{
i = 1;
if(j>0)
{
m++;
Console.WriteLine("All 2 Events Raised! - iteration " + m.ToString());
}
RaiseSignalMyManualEvent(1);
}
}
//if MyManualEventList[2] has not been signaled, wait something to happen, and signal it.
while(j == 0)
{
Random rnd = new Random();
k = rnd.Next();
if(k % 617 == 0)
{
j = 1;
m++;
Console.WriteLine("All 2 Events Raised! - iteration " + m.ToString());
RaiseSignalMyManualEvent(2);
}
}
}
}
public class Counter //Provide a number to record iteration
{
public static int m = 0;
}
}
Result:
Sorry for do not have enough reputation to post images.
At the line where there's a breakpoint, system throws exception " the given key is not in dictionary". this exception happens randomly, sometimes because th1 disposed <2, MyManualEvent> or th2 disposed <1, MyManualEvent> , sometimes none has been disposed but it just cannot find anyone.
I run this program 3 times, exception happens at iteration12, iteration45, and iteration0 (at the beginning).
OK 2 answers
1: Your code returns "Event 1" after "All events", because the two console.writelines are in a race condition (the last while loop is never iterated)
2: the 'System' distingushes between the two ManualResetEvent objects becuase it references the SortedList you put them in. ie.
static SortedList<int, ManualResetEvent> MyManualEventList
= new SortedList<int, ManualResetEvent>();
public EventHolder() { Signaler.SignalMyManualEvent
+= OnSignalMyManualEvent; }
void OnSignalMyManualEvent(int listindex)
{
MyManualEventList[listindex].Set();
}
when you raise event 1 you call set on Item 1 in the SortedList and when you raise Event 2 you call set on Item 2 in the list.
this is bad because the calling code has no idea which thread it is allowing to continue and you could well get a null exception

Multithread access to list C#

I have a task to show difference between syncronized and unsyncronized multithreading. Therefore I wrote an application simulating withdrawing money from clients' bank accounts. Each of some number of threads chooses a random user and withdraws money from the account.
Every thread should withdraw every account once. First time the threads are syncronized, but the second time they are not. So there must be a difference between accounts, withdrawed by syncronized and unsyncronized threads. And the difference must be different for different numbers of users and threads. But in my application I have difference just for 1000 threads. So I need unsyncronized threads' results to be strongly different from syncronized threads' ones.
The class User:
public class User : IComparable
{
public string Name { get; set; }
public int Start { get; set; }
public int FinishSync { get; set; }
public int FinishUnsync { get; set; }
public int Hypothetic { get; set; }
public int Differrence { get; set; }
...
}
The method which withdraws money:
public void Withdraw(ref List<User> users, int sum, bool isSync)
{
int ind = 0;
Thread.Sleep(_due);
var rnd = new Random(DateTime.Now.Millisecond);
//used is list of users, withrawed by the thread
while (_used.Count < users.Count)
{
while (_used.Contains(ind = rnd.Next(0, users.Count))) ; //choosing a random user
if (isSync) //isSync = if threads syncroized
{
if (Monitor.TryEnter(users[ind]))
{
try
{
users[ind].FinishSync = users[ind].FinishSync - sum;
}
finally
{
Monitor.Exit(users[ind]);
}
}
}
else
{
lock (users[ind])
{
users[ind].FinishUnsync = users[ind].FinishUnsync - sum;
}
}
_used.Add(ind);
}
done = true;
}
And the threads are created this way:
private void Withdrawing(bool IsSync)
{
if (IsSync)
{
for (int i = 0; i < _num; i++)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, true); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
_threads[i].Join();
}
}
else
{
for (int i = 0; i < _num; ++i)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, false); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
}
}
I've changed the Withdraw class this way, bc the problem could have been in creating threads separately from the delegate:
class Withdrawer
{
private List<int>[] _used;
private int _due;
private int _pause;
public int done;
private List<Thread> _threads;
public Withdrawer(List<User> users, int n, int due, int pause, int sum)
{
_due = due;
_pause = pause;
done = 0;
_threads = new List<Thread>(users.Count);
InitializeUsed(users, n);
CreateThreads(users, n, sum, false);
_threads.Clear();
while (done < n) ;
Array.Clear(_used,0,n-1);
InitializeUsed(users, n);
CreateThreads(users, n, sum, true);
}
private void InitializeUsed(List<User> users, int n)
{
_used = new List<int>[n];
for (int i = 0; i < n; i++)
{
_used[i] = new List<int>(users.Count);
for (int j = 0; j < users.Count; j++)
{
_used[i].Add(j);
}
}
}
private void CreateThreads(List<User> users, int n, int sum, bool isSync)
{
for (int i = 0; i < n; i++)
{
_threads.Add(new Thread(delegate() { Withdraw(users, sum, isSync); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
}
public void Withdraw(List<User> users, int sum, bool isSync)
{
int ind = 0;
var rnd = new Random();
while (_used[int.Parse(Thread.CurrentThread.Name)].Count > 0)
{
int x = rnd.Next(_used[int.Parse(Thread.CurrentThread.Name)].Count);
ind = _used[int.Parse(Thread.CurrentThread.Name)][x];
if (isSync)
{
lock (users[ind])
{
Thread.Sleep(_due);
users[ind].FinishSync -= sum;
}
}
else
{
Thread.Sleep(_due);
users[ind].FinishUnsync -= sum;
}
_used[int.Parse(Thread.CurrentThread.Name)][x] = _used[int.Parse(Thread.CurrentThread.Name)][_used[int.Parse(Thread.CurrentThread.Name)].Count - 1];
_used[int.Parse(Thread.CurrentThread.Name)].RemoveAt(_used[int.Parse(Thread.CurrentThread.Name)].Count - 1);
Thread.Sleep(_pause);
}
done++;
}
}
Now the problem is FinishUnSync values are correct, while FinishSync values are absolutely not.
Thread.Sleep(_due);
and
Thread.Sleep(_pause);
are used to "hold" the resourse, bc my task is the thread should get resourse, hold it for a _due ms, and after processing wait _pause ms before finishing.
Your code isn't doing anything useful, and doesn't show the difference between synchronized and unsynchronized access. There are many things you'll need to address.
Comments in your code say that _used is a list of users that have been accessed by the thread. You're apparently creating that on a per-thread basis. If that's true, I don't see how. From the looks of things I'd say that _used is accessible to all threads. I don't see anywhere that you're creating a per-thread version of that list. And the naming convention indicates that it's at class scope.
If that list is not per-thread, that would go a long way towards explaining why your data is always the same. You also have a real race condition here because you're updating the list from multiple threads.
Assuning that _used really is a per-thread data structure . . .
You have this code:
if (isSync) //isSync = if threads syncroized
{
if (Monitor.TryEnter(users[ind]))
{
try
{
users[ind].FinishSync = users[ind].FinishSync - sum;
}
finally
{
Monitor.Exit(users[ind]);
}
}
}
else
{
lock (users[ind])
{
users[ind].FinishUnsync = users[ind].FinishUnsync - sum;
}
}
Both of these provide synchronization. In the isSync case, a second thread will fail to do its update if a thread already has the user locked. In the second case, the second thread will wait for the first to finish, and then will do the update. In either case, the use of Monitor or lock prevents concurrent update.
Still, you would potentially see a difference if multiple threads could be executing the isSync code at the same time. But you won't see a difference because in your synchronized case you never let more than one thread execute. That is, you have:
if (IsSync)
{
for (int i = 0; i < _num; i++)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, true); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
_threads[i].Join();
}
}
else
{
for (int i = 0; i < _num; ++i)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, false); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
}
So in the IsSync case, you start a thread and then wait for it to complete before you start another thread. Your code is not multithreaded. And in the "unsynchronized" case you're using a lock to prevent concurrent updates. So in one case you prevent concurrent updates by only running one thread at a time, and in the other case you prevent concurrent updates by using a lock. There will be no difference.
Something else worth noting is that your method of randomly selecting a user is highly inefficient, and could be part of the problem you're seeing. Basically what you're doing is picking a random number and checking to see if it's in a list. If it is, you try again, etc. And the list keeps growing. Quick experimentation shows that I have to generate 7,000 random numbers between 0 and 1,000 before I get all of them. So your threads are spending a huge amount of time trying to find the next unused account, meaning that they have less likelihood to be processing the same user account at the same time.
You need to do three things. First, change your Withdrawl method so it does this:
if (isSync) //isSync = if threads syncroized
{
// synchronized. prevent concurrent updates.
lock (users[ind])
{
users[ind].FinishSync = users[ind].FinishSync - sum;
}
}
else
{
// unsynchronized. It's a free-for-all.
users[ind].FinishUnsync = users[ind].FinishUnsync - sum;
}
Your Withdrawing method should be the same regardless of whether IsSync is true or not. That is, it should be:
for (int i = 0; i < _num; ++i)
{
_withdrawers.Add(new Withdrawer(Users.Count, _due, _pause));
_threads.Add(new Thread(delegate()
{ _withdrawers[i].Withdraw(ref Users, _sum, false); }));
_threads[i].Name = i.ToString();
_threads[i].Start();
}
Now you always have multiple threads running. The only difference is whether access to the user account is synchronized.
Finally, make your _used list a list of indexes into the users list. Something like:
_used = new List<int>(users.Count);
for (int i = 0; i < _used.Count; ++i)
{
_used[i] = i;
}
Now, when you select a user, you do this:
var x = rnd.Next(_used.Count);
ind = _used[x];
// now remove the item from _used
_used[x] = _used[_used.Count-1];
_used.RemoveAt(_used.Count-1);
That way you can generate all users more efficiently. It will take n random numbers to generate n users.
A couple of nitpicks:
I have no idea why you have the Thread.Sleep call in the Withdraw method. What benefit do you think it provides?
There's no real reason to pass DateTime.Now.Millisecond to the Random constructor. Just calling new Random() will use Environment.TickCount for the seed. Unless you really want to limit the seed to numbers between 0 and 1,000.

.NET 2.0 Processing very large lists using ThreadPool

This is further to my question here
By doing some reading .... I moved away from Semaphores to ThreadPool.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ThreadPoolTest
{
class Data
{
public int Pos { get; set; }
public int Num { get; set; }
}
class Program
{
static ManualResetEvent[] resetEvents = new ManualResetEvent[20];
static void Main(string[] args)
{
int s = 0;
for (int i = 0; i < 100000; i++)
{
resetEvents[s] = new ManualResetEvent(false);
Data d = new Data();
d.Pos = s;
d.Num = i;
ThreadPool.QueueUserWorkItem(new WaitCallback(Process), (object)d);
if (s >= 19)
{
WaitHandle.WaitAll(resetEvents);
Console.WriteLine("Press Enter to Move forward");
Console.ReadLine();
s = 0;
}
else
{
s = s + 1;
}
}
}
private static void Process(object o)
{
Data d = (Data) o;
Console.WriteLine(d.Num.ToString());
Thread.Sleep(10000);
resetEvents[d.Pos].Set();
}
}
}
This code works and I am able to process in the sets of 20. But I don't like this code because of WaitAll. So let's say I start a batch of 20, and 3 threads take longer time while 17 have finished. Even then I will keep the 17 threads as waiting because of the WaitAll.
WaitAny would have been good... but it seems rather messy that I will have to build so much of control structures like Stacks, Lists, Queues etc in order to use the pool efficiently.
The other thing I don't like is that whole global variable in the class for resetEvents. because this array has to be shared between the Process method and the main loop.
The above code works... but I need your help in improving it.
Again... I am on .NET 2.0 VS 2008. I cannot use .NET 4.0 parallel/async framework.
There are several ways you can do this. Probably the easiest, based on what you've posted above, would be:
const int MaxThreads = 4;
const int ItemsToProcess = 10000;
private Semaphore _sem = new Semaphore(MaxThreads, MaxThreads);
void DoTheWork()
{
int s = 0;
for (int i = 0; i < ItemsToProcess; ++i)
{
_sem.WaitOne();
Data d = new Data();
d.Pos = s;
d.Num = i;
ThreadPool.QueueUserWorkItem(Process, d);
++s;
if (s >= 19)
s = 0;
}
// All items have been assigned threads.
// Now, acquire the semaphore "MaxThreads" times.
// When counter reaches that number, we know all threads are done.
int semCount = 0;
while (semCount < MaxThreads)
{
_sem.WaitOne();
++semCount;
}
// All items are processed
// Clear the semaphore for next time.
_sem.Release(semCount);
}
void Process(object o)
{
// do the processing ...
// release the semaphore
_sem.Release();
}
I only used four threads in my example because that's how many cores I have. It makes little sense to be using 20 threads when only four of them can be processing at any one time. But you're free to increase the MaxThreads number if you like.
So I'm pretty sure this is all .NET 2.0.
We'll start out defining Action, because I'm so used to using it. If using this solution in 3.5+, remove that definition.
Next, we create a queue of actions based on the input.
After that we define a callback; this callback is the meat of the method.
It first grabs the next item in the queue (using a lock since the queue isn't thread safe). If it ended up having an item to grab it executes that item. Next it adds a new item to the thread pool which is "itself". This is a recursive anonymous method (you don't come across uses of that all that often). This means that when the callback is called for the first time it will execute one item, then schedule a task which will execute another item, and that item will schedule a task that executes another item, and so on. Eventually the queue will run out, and they'll stop queuing more items.
We also want the method to block until we're all done, so for that we keep track of how many of these callbacks have finished through incrementing a counter. When that counter reaches the task limit we signal the event.
Finally we start N of these callbacks in the thread pool.
public delegate void Action();
public static void Execute(IEnumerable<Action> actions, int maxConcurrentItems)
{
object key = new object();
Queue<Action> queue = new Queue<Action>(actions);
int count = 0;
AutoResetEvent whenDone = new AutoResetEvent(false);
WaitCallback callback = null;
callback = delegate
{
Action action = null;
lock (key)
{
if (queue.Count > 0)
action = queue.Dequeue();
}
if (action != null)
{
action();
ThreadPool.QueueUserWorkItem(callback);
}
else
{
if (Interlocked.Increment(ref count) == maxConcurrentItems)
whenDone.Set();
}
};
for (int i = 0; i < maxConcurrentItems; i++)
{
ThreadPool.QueueUserWorkItem(callback);
}
whenDone.WaitOne();
}
Here's another option that doesn't use the thread pool, and just uses a fixed number of threads:
public static void Execute(IEnumerable<Action> actions, int maxConcurrentItems)
{
Thread[] threads = new Thread[maxConcurrentItems];
object key = new object();
Queue<Action> queue = new Queue<Action>(actions);
for (int i = 0; i < maxConcurrentItems; i++)
{
threads[i] = new Thread(new ThreadStart(delegate
{
Action action = null;
do
{
lock (key)
{
if (queue.Count > 0)
action = queue.Dequeue();
else
action = null;
}
if (action != null)
{
action();
}
} while (action != null);
}));
threads[i].Start();
}
for (int i = 0; i < maxConcurrentItems; i++)
{
threads[i].Join();
}
}

Categories