I tried to use the ReaderWriterCollection in some update I have currently.
The Thread Safe object can't do all the job for the coder, there is still easy errors that can happens especially when we try to update a collection concurrently with an other thread... (if we keep the locks atomic when updating we can run into race condition, if we locks it "widely" we get back to previous lock() behavior...)
But I think it should prevent any collection from being corrupted while reading it, and atomic update should take advantage of this new locks without getting in race conditions using some AddorUpdate() or AddIfNotExists() method which are safe for race conditions ! (these methods are here to make the CHECK before an UPDATE in the same lock, this can't be obtain using "Getters")
---
On an other Area I started to check is How to use "Parallel LINQ" this is an extension to LINQ assembly which enable multi-threading on Collections manipulations...
The use-cases seems pretty specific, either a big collection that we can handle fast, or a small collection that is iterated over a heavy method (and obviously a huge collections with heavy work...)
Parallel LINQ requires a lot of thread safety, which mean it should iterate over a Safe-Thread Read collection, and append its results in a Safe Concurrent Write Collection ! That's where .NET Concurrent Colleciton are absolutely necessary, they are made for Concurrent write !
Parallel queries can be ordered or unordered, but ordered behavior prevent a lot of performance gain through parallelism, for now the best use case I found for this is some Skillbase method that needs to instanciate a Game Skill Index from a Database result
you can call some New() Method in a heavy threaded environment this is always thread safe as you'll work on an other object instance, thus creating objects from a read only data result in multi-thread is pretty easy !
Here is my "GetSpellList" Method, Spell list can be the example of "lot of small object to handle" even if it's more a "Proof of Concept"
I make a copy of the spell list I need to work on, then prepare a concurrent bag to get result,
The AsParallel().ForAll() does all the job, it's just like a foreach but in multi-thread, in there I just make a spell Clone then store it in the safe ConcurrentBag...
Once the Paralell query is finished I use the concurrent bag to retrieve a "List" of results for the function...
- Code: Select all
public static List<Spell> GetSpellList(string spellLineID)
{
m_syncLockUpdates.EnterReadLock();
List<Spell> spellList = new List<Spell>();
try
{
if (m_lineSpells.ContainsKey(spellLineID))
spellList = new List<Spell>(m_lineSpells[spellLineID]);
}
finally
{
m_syncLockUpdates.ExitReadLock();
}
ConcurrentBag<Spell> results = new ConcurrentBag<Spell>();
spellList.AsParallel().ForAll(item =>
{
try
{
results.Add((Spell)item.Clone());
}
catch
{
}
});
return results.OrderBy(el => el.Level).ThenBy(el => el.ID).ToList();
}
In Class RealmAbilities it's the scenario "low-count / heavy work" for each ability we need to provide for a class I'm going to call the Assembly Activator to get custom object instances, I assume this is somewhat slower than a copy/increment or other basic update...
I Use a concurrent bag to get results the same as above, here in the parallel query I provide DB object I retrieved in a copy before, and give them to Ability GetInstance(), to build the new ability instance, this is safe because every thread will work on a different object, and then add it to the concurrent bag for results... (Casted to list with LINQ... pretty easy !)
- Code: Select all
public static List<RealmAbility> GetClassRealmAbilities(int classID)
{
List<DBAbility> ras = new List<DBAbility>();
m_syncLockUpdates.EnterReadLock();
try
{
if (m_classRealmAbilities.ContainsKey(classID))
{
foreach (string str in m_classRealmAbilities[classID])
{
try
{
ras.Add(m_abilityIndex[str]);
}
catch
{
}
}
}
}
finally
{
m_syncLockUpdates.ExitReadLock();
}
ConcurrentBag<RealmAbility> returns = new ConcurrentBag<RealmAbility>();
ras.AsParallel().ForAll(element =>
{
try
{
RealmAbility rab = (RealmAbility)GetNewAbilityInstance(element);
rab.Level = 1;
returns.Add(rab);
}
catch
{
}
});
return returns.OrderBy(el => el.ID).ToList();
}