I have a class as:
class SomeClass{
class Connection{//some fields}
static List<Connection> connections { get; set; }
public SomeClass( \\params etc)
{
connections = new List<Connections>(); // initialize connections list
//initialize some other private vars
// ...
mainClassThreadMethod();
}
private void mainClassThreadMethod()
{
while (true)
{
Thread t;
Connection p = new Connection ( { \\instantiate the class})
// this code will not execute until p is initialized... In other words this loop will not execute several times quickly.
t = new Thread(new ParameterizedThreadStart(startThread));
t.Start(p);
}
}
private void startThread(object o)
{
//add a new connection to the list
connections.Add((Connection)o));
}
public List<Connection> getConnections()
{
return connections;
}
}
why is it that after adding new connections to the list if I then call the getConnections method it returns a null list? I figure it is because I am adding the items from a different thread. How can I keep track of this?
You have several problems in the code above, but sticking to the question asked, to synchronize the list (allow adding from different threads) you can (1) implement your own locking, or (2) use http://msdn.microsoft.com/en-us/library/3azh197k.aspx.
I'd go for #2, but in your case:
No, it's probably not caused by adding anything from a different thread
Why would you even want to add from inside startThread? You have the connection object before you instantiate a thread, so you can easily call connections.Add(connection) from the same thread thereby eliminating the need for any locking.
Why is there a while(true) loop around the thread spin-up process?
Related
I've search across the web and I get a bit confused about multithreading (lock, Monitor.Enter, volatile etc.) So, instead of asking the solution here, I've tried something "homemade" about multithreading management and I would like to have your advices.
Here's my context :
-I have a static class that contains a static Dictionary<int,string>
-I have a lot of tasks (let's say 1000) reading in this Dictionary every seconds
-I have one another task, that will update this Dictionary every 10s.
Here's the code of the cache:
public static class Cache
{
public static bool locked = false;
public static Dictionary<int, string> Entries = new Dictionary<int, string>();
public static Dictionary<int, string> TempEntries = new Dictionary<int, string>();
// Called by 1000+ Tasks
public static string GetStringByTaskId(int taskId)
{
string result;
if (locked)
TempEntries.TryGetValue(taskId, out result);
else
Entries.TryGetValue(taskId, out result);
return result;
}
// Called by 1 task
public static void UpdateEntries(List<int> taskIds)
{
TempEntries = new Dictionary<int, string>(Entries);
locked = true;
Entries.Clear();
try
{
// Simulates database access
Thread.Sleep(3000);
foreach (int taskId in taskIds)
{
Entries.Add(taskId, $"task {taskId} : {DateTime.Now}");
}
}
catch (Exception ex)
{
Log(ex);
}
finally
{
locked = false;
}
}
}
There's my questions :
The program runs but I don't understand why the twice 'locked' bool assignments in the UpdateEntries methods doesn't generate a multithreading exception since it's read by the other thread "everytime"
Is there a more conventional way to handle this, I feel like it's a weird way to do it?
The conventional way to handle this is to use a ConcurrentDictionary. This class is thread-safe and designed for multiple threads to read and write to it. You still need to be aware of potential logic problems (e.g. if two keys must be added at the same time before other threads can see either of them), but it is going to be fine for most operations without additional locking.
Another way to handle this for your specific situation is to use an ordinary dictionary, but treat it as immutable once it is available to reader threads. This will be more efficient as it avoids locks.
public static void UpdateEntries(List<int> taskIds)
{
//Other threads can't see this dictionary
var transientDictionary = new Dictionary<int, string>();
foreach (int taskId in taskIds)
{
transientDictionary.Add(taskId, $"task {taskId} : {DateTime.Now}");
}
//Publish the new dictionary so other threads can see it
TempEntries = transientDictionary;
}
Once the dictionary is assigned to TempEntries (the only place other threads can access it), it is never modified, so the threading concerns go away.
Using a non-volatile bool flag for thread synchronization is not thread-safe, and makes your code susceptible to race conditions and haisenbugs. The correct way to do it is to replace atomically the old dictionary with the new one, after the new one has been fully constructed, using a cross-thread publishing mechanism like the Volatile.Write or the Interlocked.Exchange methods. Your case is simple enough that you could also use the volatile keyword for brevity, like in the example below:
public static class Cache
{
private static volatile ReadOnlyDictionary<int, string> _entries
= new ReadOnlyDictionary<int, string>(new Dictionary<int, string>());
public static IReadOnlyDictionary<int, string> Entries => _entries;
// Called by 1000+ Tasks
public static string GetStringByTaskId(int taskId)
{
_entries.TryGetValue(taskId, out var result);
return result;
}
// Called by 1 task
public static void UpdateEntries(List<int> taskIds)
{
Thread.Sleep(3000); // Simulate database access
var temp = new Dictionary<int, string>();
foreach (int taskId in taskIds)
{
temp.Add(taskId, $"task {taskId} : {DateTime.Now}");
}
_entries = new ReadOnlyDictionary<int, string>(temp);
}
}
With this approach every access to the _entries field will incur the cost of volatility, which is typically less than 10 nsec per operation, so it shouldn't be a problem. It's a cost worth paying, because it guarantees the correctness of your program.
For an implementation of a games browser I need a collection of games that can be read and written by different threads. I want the elements of the collection to be as independent of each other as possible. I feel like changing one game should not disturb another game. Thats why I am hesitating to make use of the ReaderWriterLockSlim class when changing a game. As of now I am using a combination of a simple lock object within the game class and the ReaderWriterLockSlim class as follows:
Consumer: Multiple threads. One for every client that requests the collection of games. They are using EnterReadLock.
Producer (add): One thread for adding new games to the collection if required. After it finishes its work it will be called through a timer later again. It is using EnterUpgradeableReadLock once and EnterWriteLock for every required new game.
Producer (modify): Multiple threads. Up to at least one for every game that exists in the collection. Every thread is always solely going to change one game. There may be multiple threads for one game. To access the right game in the collection they are making use of EnterReadLock. To prevent multiple threads from interfering when processing the same game, I am using an additional simple lock object on the game class right now.
As of this article Are IEnumerable Linq methods thread-safe? I know, that modifying the collection invalidates the enumerator and its behavior becomes undefined. But I am not sure, how to solve it properly.
So my Problem is: Modifying producers may change the elements of the collection while either
adding producers may add new elements or
consumers may iterate over the collection via linq expressions.
What do you suggest? Does a thread that wants to change a game really have to EnterWriteLock, thus preventing other games from being updated at the same time? Am I too concerned about performance issues?
Sample code:
public class Game
{
private readonly IList<Player> players;
public Game(int id, string name, GameMode mode)
{
ID = id;
Name = name;
Mode = mode;
Status = GameStatus.WaitingForPlayers;
players = new List<Player>();
}
public object Lock { get; private set; } = new object();
public int ID { get; }
public string Name { get; }
public GameMode Mode { get; }
public GameStatus Status { get; private set; }
public int PlayerCount => players.Count;
public void Join(Player player)
{
// Just an example...
players.Add(player);
}
public void Leave(Player player)
{
players.Remove(player);
}
public void Start()
{
// Just an example...
Status = GameStatus.Started;
}
public GameDto ToDto() => new GameDto(ID, Name, Mode, Status, PlayerCount);
// Just an example. Depends on if it was started already and so on...
public bool CanStart => PlayerCount > 3;
}
public class Server : IDisposable
{
private readonly ReaderWriterLockSlim gamesLock = new ReaderWriterLockSlim();
// This is the collection, that is giving me headaches.
private ICollection<Game> games = new List<Game>();
// ... Constructor and more ...
// Will be called once in a while but not in parallel. Adds games, if available games are full...
private void AddRequiredGames()
{
while (TryGetRequiredGame(GetGames(), out Game game))
{
gamesLock.EnterWriteLock();
games.Add(game);
gamesLock.ExitWriteLock();
}
}
// Will be called once the last player left a game. There may be multiple threads at once for different games.
private void RemoveIfObsolete(Game game)
{
gamesLock.EnterUpgradeableReadLock();
lock (game.Lock)
{
#warning Here I am eventually iterating over the collection, while its elements could be modified.
if (game.IsEmpty && games.Count(item => item.Mode == game.Mode
&& item.Status == GameStatus.WaitingForPlayers) > 3)
{
// ToDo - Mark game as obsolete to prevent incoming join attempts.
gamesLock.EnterWriteLock();
games.Remove(game);
gamesLock.ExitWriteLock();
}
}
gamesLock.ExitUpgradeableReadLock();
}
private ICollection<GameDto> GetGames()
{
gamesLock.EnterReadLock();
#warning Here I am iterating over the collection, while its elements could be modified.
IList<GameDto> result = games.Select(item => item.ToDto()).ToList();
gamesLock.ExitReadLock();
return result;
}
// Will be called asynchronously be every client that requests to join a game.
private void HandlePlayerJoin(Player player, Game game)
{
lock (game.Lock)
{
#warning Here I am modifying an element of the collection without locking the collection.
game.Join(player);
}
// Just an example. Wouldn't try to start the game, if the player could not join etc...
TryStartGame(game);
}
private void TryStartGame(Game game)
{
lock (game.Lock)
{
if (game.CanStart)
#warning Here I am modifying an element of the collection without locking the collection.
game.Start();
}
}
// Will be called asynchronously be every client that requests the list of games.
private void HandleGetGames(Client client)
{
client.Send(new GetGamesResponse(GetGames()));
}
}
I am trying to determine a solution to a problem I am facing outlined in this Programmers.SE question. The specific problem I am facing right now is that I need someway to make multiple atomic modifications to collections from the System.Collections.Concurrent namespace. From what I can tell there is no mechanism in place to do so; concurrent collections only guarantee that single operations are atomic.
One solution to the problem I do not wish to have to implement is to create my own concurrent collection that provides some mechanism or method for multiple atomic operations. I'd like to think I am experienced enough to write my own concurrent collection to allow multiple atomic modifications, though I would much rather use out of the box, well developed classes.
In light of this I have thought of another possible solution using the out of the box collections provided. My solution is to use a lock to control access to parts of code that perform multiple modifications so that they do not interleave each other.
public interface IWork { }
public interface IResource { }
public sealed class WorkPerformer
{
public static WorkPerformer Instance { get { return lazyInstance.Value; } }
public static readonly Lazy<WorkPerformer> lazyInstance = new Lazy<WorkPerformer>(() => new WorkPerformer());
private ConcurrentDictionary<IResource, ConcurrentQueue<Guid>> IResourceWaitQueues { get; set; }
private ConcurrentDictionary<IWork, ConcurrentDictionary<IResource, Guid>> IWorkToPerform { get; set; }
private readonly object _LockObj = new object();
private WorkPerformer()
{
IResourceWaitQueues = new ConcurrentDictionary<IResource, ConcurrentQueue<Guid>>();
IWorkToPerform = new ConcurrentDictionary<IWork, ConcurrentDictionary<IResource, Guid>>();
}
private void ModifierTask_MultipleAdds(IWork workToDo)
{
Task.Run(() =>
{
lock(_LockObj)
{
// -- The point is here I am making multiple additions to IResourceWaitQueues and IWorkToPerform
// Find all IResource this IWork uses and generate a Guid for each
// Enqueue these Guid into their respective ConcurrentQueue's within IResourceWaitQueues
// Add this IWork and IResource -> Guid mapping into IWorkToPerform
}
});
}
public void ModifierTask_MultipleRemoves(IWork workThatsDone)
{
Task.Run(() =>
{
lock (_LockObj)
{
// -- The point is here I am making multiple deletions to IResourceWaitQueues and IWorkToPerform
// Find all IResource that this IWork used to perform its work
// Dequeue from the ConcurrentQueue respective to each IResource used from IResourceWaitQueues
// Remove this ITask KeyValuePair from IWorkToPerform
}
});
}
}
I was wondering if this solution would work to allow multiple atomic operations to IResourceWaitQueues and IWorkToPerform in the example code above?
I would have to assume that it could turn out to be slow sometimes if there are multiple contentions for the lock. But other than that if I understand lock correctly these the multiple modifications I wish to perform should not interleave each other because only one thread should be allowed in the locked code at a time.
The only other issue I see is that I think I would have to lock on every other access to IResourceWaitQueues and IWorkToPerform in the example code above? Unless of course it is ok for the accesses to be interleaved with the locked portions of code.
EDIT: Here is a much more complete code example with some, hopefully, helpful comments on the exact problem I am trying to solve. Again for reference an alternatively worded explanation of the problem and solution is outlined in this Programmers.SE question I asked.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace WorkProcessorSandbox
{
public interface IResource { }
public interface IWork
{
void ProcessWork();
List<IResource> NeededResources { get; set; }
}
// This classes purpose is to process IWork objects by calling their ProcessWork methods when it is found that
// the IResources they need to process are free. The nature of IResource objects is that they are not threadsafe
// (though some may be; some must be if an IResource appears in NeededResources multiple times). As a result
// care must be taken to make sure two IWork do not try to use a distinct IResource simultaneously.
// This is done by a sort of signalling/ticketing system. Each time a new IWork comes in to be processed it "lines
// up" for the IResources it needs. Only when it is at the front of the line for all IResource it needs will it
// move on to process. By forcing atomicity on the "lining up" of the IWork for IResources deadlocks and race conditions
// can be prevented because the order of an IWork "ticket" in a line can never interleave anothers.
public sealed class WorkProcessor
{
// Singleton class
public static WorkProcessor Instance { get { return lazyInstance.Value; } }
public static readonly Lazy<WorkProcessor> lazyInstance = new Lazy<WorkProcessor>(() => new WorkProcessor());
// ResourceWaitQueues holds a Queue of Guids mapped to distinct
// IResources representing the next IWork that is in line to use it
private readonly object _Lock_ResourceDict = new object();
private Dictionary<IResource, Queue<Guid>> ResourceWaitQueues { get; set; }
// WorkToProcess holds a Dictionary of Guid mapped to IResources representing
// the place in line this IWork (that said Dictionary is mapped to) is in for use of the IResources.
private readonly object _Lock_WorkDict = new object();
private Dictionary<IWork, Dictionary<IResource, Guid>> WorkToProcess { get; set; }
private WorkProcessor()
{
Running = false;
}
private bool Running { get; set; }
private CancellationToken ProcessingToken { get; set; }
private CancellationTokenSource ProcessingTokenSource { get; set; }
// Stops the processing of IWork from the WorkToProcess Dictionary
public void StopProcessing()
{
if (Running)
{
ProcessingTokenSource.Cancel();
Running = false;
}
}
// Starts (Allows) the processing of IWork from the WorkToProcess Dictionary
public void StartProcessing()
{
if (!Running)
{
// Instantiate to Empty
ResourceWaitQueues = new Dictionary<IResource, Queue<Guid>>();
WorkToProcess = new Dictionary<IWork, Dictionary<IResource, Guid>>();
// Create CancellationToken for use in controlling Tasks
ProcessingTokenSource = new CancellationTokenSource();
ProcessingToken = ProcessingTokenSource.Token;
Running = true;
}
}
// The purpose of this method is to compare the list of Guids at the front of the Queues in ResourceWaitQueues
// to the list of Guids that each IWork is waiting on for it to start processing.
// If the Guids that an IWork needs to start processing is present in the list of Guids at the front of the Queues
// then the IWork can start processing, otherwise it cannot.
private void TryProcessWork()
{
if(Running)
{
// A Task that will go through all of the IWork waiting to be
// processed and start processing the IWork objects that are ready.
Task.Run(() =>
{
// Here we need to lock on both the ResourceWaitQueues and WorkToProcess locks
lock (_Lock_ResourceDict) {
lock (_Lock_WorkDict)
{
// Go through the Dictionary of IWork waiting to be processed
foreach (var waitingWork in WorkToProcess)
{
// Find the List<Guid> that are needed for this IWork to be processed
var worksGuids = waitingWork.Value.Select(x => x.Value).ToList();
// Find the List<Guid> that are currently ready to be processed
var guidsReadyToProcess = ResourceWaitQueues.Values.Select(x =>
{
// If a Queue<T> is Empty when it is Peek'd it throws and Exception!
if (x.Count > 0)
return x.Peek();
return Guid.Empty;
}).ToList();
// If the List<Guid> needed by this IWork is contained within the List<Guid> ready to be processed
if (worksGuids.All(x => guidsReadyToProcess.Contains(x)))
{
// This IWork is ready to be processed!
ProcessWork(waitingWork);
// Remove this IWork from WorkToProcess
if (!WorkToProcess.Remove(waitingWork.Key))
{
Console.Out.WriteLine("Fatal error! Stopping work processing. Could not remove IWork from Dictionary that should contain it.");
StopProcessing();
break;
}
}
}
}
}
}, ProcessingToken);
}
}
// The purpose of this function is to "enqueue" IWork for processing. First a list of all the IResources
// that the IWork needs to process is created along with a Guid for each unique IResource it uses.
// These Guids are then enqueued into the respective Queue in ResourceWaitQueues representing this IWork's
// "spot in line" to use those specific IResources. Finally the IWork and its Guids are then added to the
// WorkToPerform Dictionary so that TryProcessWork can determine if it is ready to run or not.
// TryProcess is called at the end to see if this IWork is possibly ready to process right away.
public void EnqueueWork(IWork workToDo)
{
if (Running)
{
// Get all distinct IResource in the IWork's NeededResources
var worksResources = workToDo.NeededResources.Distinct().ToList();
// Create the Guids this IWork object will wait on to start processing
Dictionary<IResource, Guid> worksGuidResourceMap = new Dictionary<IResource, Guid>();
worksResources.ForEach(x => worksGuidResourceMap.Add(x, Guid.NewGuid()));
// Here we need to lock on both the ResourceWaitQueues and WorkToProcess locks
lock (_Lock_ResourceDict) {
lock (_Lock_WorkDict)
{
// Find all of the IResources that are not currently present in the ResourceWaitQueues Dictionary
var toAddResources = worksResources.Where(x => !ResourceWaitQueues.Keys.Contains(x)).ToList();
// Create a new entry in ResourceWaitQueues for these IResources
toAddResources.ForEach(x => ResourceWaitQueues.Add(x, new Queue<Guid>()));
// Add each Guid for this works IResources into the Queues in ResourceWaitQueues
foreach (var aGuidResourceMap in worksGuidResourceMap)
{
foreach (var resourceQueue in ResourceWaitQueues)
{
if (aGuidResourceMap.Key == resourceQueue.Key)
resourceQueue.Value.Enqueue(aGuidResourceMap.Value);
}
}
// Add this IWork and its processing info to the Dictionary of awaiting IWork to be processed
WorkToProcess.Add(workToDo, worksGuidResourceMap);
}
}
// Go through the list of IWork waiting to be processed and start processing IWork that is ready
TryProcessWork();
}
}
// The purpose of this function is to create a Task in which the IWork passed to it can be processed.
// Once the processing is complete the Task then dequeues a single Guid from the Queue respective to
// each IResource it needed to process. It then calls TryProcessWork because it is most likely possible
// there is some IWork that is now ready to process.
private void ProcessWork(KeyValuePair<IWork, Dictionary<IResource, Guid>> workToProcess)
{
Task.Run(() =>
{
// Actually perform the work to be processed.
workToProcess.Key.ProcessWork();
// Get the list of the IResources that were used during processing
var usedResources = workToProcess.Value.Select(x => x.Key).ToList();
// We are removing multiple Guids from the ResourceWaitQueues. They must be atomic.
// The ResourceWaitQueues could become incoherent if any other operations are performed on it during the dequeueing.
// It is ok for WorkToProcess to be modified while this is happening.
lock (_Lock_ResourceDict)
{
// Get the Queues corresponding to these IResources
var resourceQueues = ResourceWaitQueues.Where(x => usedResources.Contains(x.Key)).Select(x => x.Value).ToList();
try
{
// Dequeue a Guid from each of these Queues exposing the next Guid to be processed on each
resourceQueues.ForEach(x => x.Dequeue());
}
catch (InvalidOperationException ex)
{
Console.Out.WriteLine("Fatal error! Stopping work processing. Could not dequeue a Guid that should exist: " + ex.Message);
StopProcessing();
}
}
// Go through the list of IWork waiting to be processed and start processing IWork that is ready
TryProcessWork();
}, ProcessingToken);
}
}
}
Without a good Minimal, Complete, and Verifiable code example that accurately illustrates your scenario, it's impossible to say for sure. But based on your description so far, it seems reasonably clear that using lock will address your primary issue (atomicity of some grouped series of operations)
Whether you need to also use lock on all other accesses of the same objects depends on what those accesses are and how they relate to the grouped operations you're protecting with lock. For sure, there is no need to use lock just to make sure that the collections remain coherent. Their internal synchronization will ensure that.
But if your grouped operations represent some type of coherency in and of itself, where other accesses to the objects would be incorrect if they were allowed to take place while a grouped operation was in progress then, yes, you would need to also use lock, with the same _LockObj reference, to ensure that the grouped operation could not take place at the same time any other access was taking place which depended on the coherency of the data structure in aggregate.
If you need more specific advice than that, please improve the question so that it's clear how all of these operations actually relate.
Aside: you may want to consider following normal .NET coding conventions: restricting your use of Pascal casing to methods and properties, and using camel-casing for fields. This will make it easier for readers to follow your code.
I would say that for sure, it's a very bad choice to use for fields the .NET convention for interface naming (i.e. Pascal-cased identifiers which always start with I). You are sure to make it very difficult for people to understand your code when you do that.
To maximise performance you should avoid locking X number of IResource objects for the duration of the IWork.ProcessWork method. The problem is if you have an IWork object that requires 10 IResource objects, 9 of those resources may take only a few milliseconds to process while the 10th could take minutes, in this scenario, all 10 of the resource objects will be locked so that no other IWork object can use them for the full time it takes to complete the work.
By creating a LockResource method and a ReleaseResource method, you can use the ConcurrentDictionary as it's designed without the need of wrapping it in a lock as you will only be performing atomic operations, i.e. add IResource to ResourceWaitQueue and remove IResource from ResourceWaitQueue. This will allow your IWork objects to execute in an efficient way where the only bottle neck is the actual resources rather than the code.
I have a static class 'Logger' with a public property called 'LogLevels' as in code below.
When the property is used concurrently in a multi-user or multi-threaded environment, could it cause problems?
Do I need to use thread synchronization for the code within the property 'LogLevels'?
public class Logger
{
private static List<LogLevel> _logLevels = null;
public static List<LogLevel> LogLevels
{
get
{
if (_logLevels == null)
{
_logLevels = new List<LogLevel>();
if (!string.IsNullOrWhiteSpace(System.Configuration.ConfigurationManager.AppSettings["LogLevels"]))
{
string[] lls = System.Configuration.ConfigurationManager.AppSettings["LogLevels"].Split(",".ToCharArray());
foreach (string ll in lls)
{
_logLevels.Add((LogLevel)System.Enum.Parse(typeof(LogLevel), ll));
}
}
}
if (_logLevels.Count == 0)
{
_logLevels.Add(LogLevel.Error);
}
return _logLevels;
}
}
}
UPDATE: I ended up using thread synchronization to solve concurrency problem in a static class, as in code below.
public class Logger
{
private static readonly System.Object _object = new System.Object();
private static List<LogLevel> _logLevels = null;
private static List<LogLevel> LogLevels
{
get
{
//Make sure that in a multi-threaded or multi-user scenario, we do not run into concurrency issues with this code.
lock (_object)
{
if (_logLevels == null)
{
_logLevels = new List<LogLevel>();
if (!string.IsNullOrWhiteSpace(System.Configuration.ConfigurationManager.AppSettings["SimpleDBLogLevelsLogger"]))
{
string[] lls = System.Configuration.ConfigurationManager.AppSettings["SimpleDBLogLevelsLogger"].Split(",".ToCharArray());
foreach (string ll in lls)
{
_logLevels.Add((LogLevel)System.Enum.Parse(typeof(LogLevel), ll));
}
}
}
if (_logLevels.Count == 0)
{
_logLevels.Add(LogLevel.Error);
}
}
return _logLevels;
}
}
}
When the property is used concurrently in a multi-user or multi-threaded environment, could it cause problems?
Absolutely. List<T> is not designed for multiple threads, except for the case where there are just multiple readers (no writers).
Do I need to use thread synchronization for the code within the property 'LogLevels'?
Well that's one approach. Or just initialize it on type initialization, and then return a read-only wrapper around it. (You really don't want multiple threads modifying it.)
Note that in general, doing significant amounts of work in a static constructor is a bad idea. Are you happy enough that if this fails, every access to this property will fail, forever?
This code posses race conditions and cannot be safely executed from multiple threads. The primary problem is the List<T> type is not thread safe yet this code will freely write to. This mean the writes can occur in parallel and hence break the implicit contract of List<T>
The short answer is "yes" and "yes" you do need threads synchronization.
The other question is, why re-invent the wheel? You can use something like log4net or .NET logging framework.
I need to update InstrumentInfo class frequently. I update this class from one thread and access (read) from another.
I have Instrument class. For each Instrument class I need to maintain InstrumentInfo:
// omit class Instrument as not improtant
public class InstrumentInfo
{
public string Name { get; set; }
public TradingStatus Status { get; set; }
public decimal MinStep;
public double ValToday;
public decimal BestBuy;
public decimal BestSell;
}
public class DerivativeInfo : InstrumentInfo
{
public DateTime LastTradeDate { get; set; }
public DateTime ExpirationDate { get; set; }
public string UnderlyingTicker { get; set; }
}
// i do have several more subclasses
I do have two options:
Create only one InstrumentInfo for each Instrument. When some field updates, for example BestBuy just update value of this field. Clients should obtain InstrumentInfo only once and use it during entire application lifetime.
On each update create new instance of InstrumentInfo. Clients should obtain every time the most recent copy of InstrumentInfo.
With 1 I do need to lock, because decimal DateTime string update is not guaranteed to be atomic. But I don't need to reinstatiate object.
With 2 I don't need to lock at all, as reference update is atomic. But I likely will use more memory and I will probably create more work for GC because every time I need to instatiate new object (and initialize all fields).
1 implementation
private InstrumentInfo[] instrumentInfos = new InstrumentInfo[Constants.MAX_INSTRUMENTS_NUMBER_IN_SYSTEM];
// invoked from different threads
public InstrumentInfo GetInstrumentInfo(Instrument instrument)
{
lock (instrumentInfos) {
var result = instrumentInfos[instrument.Id];
if (result == null) {
result = new InstrumentInfo();
instrumentInfos[instrument.Id] = result;
}
return result;
}
}
...........
InstrumentInfo ii = GetInstrumentInfo(instrument);
lock (ii) {
ii.BestSell = BestSell;
}
2 implementation:
private InstrumentInfo[] instrumentInfos = new InstrumentInfo[Constants.MAX_INSTRUMENTS_NUMBER_IN_SYSTEM];
// get and set are invoked from different threads
// but i don't need to lock at all!!! as reference update is atomic
public void SetInstrumentInfo(Instrument instrument, InstrumentInfo info)
{
if (instrument == null || info == null)
{
return;
}
instrumentInfos[instrument.Id] = info;
}
// get and set are invoked from different threads
public InstrumentInfo GetInstrumentInfo(Instrument instrument)
{
return instrumentInfos[instrument.Id];
}
....
InstrumentInfo ii = new InstrumentInfo {
Name = ..
TradingStatus = ...
...
BestSell =
}
SetInstrumentInfo(instrument, ii); // replace InstrumentInfo
So what do you think? I want to use approach 2 because I like code without locks! Am I correct that I do not need lock at all as I just replace reference? Do you aggree that 2 is preferred? Any suggestions are welcome.
With 2 I don't need to lock at all, as reference update is atomic. But I likely will use more memory and I will probably create more work for GC because
No, your option 1 is just as likely to cause more load on the GC (by promoting more objects to the next generation).
Use the most sensible, maintainable form. In this case, create new objects.
Do not optimize based on what you 'think' might be slower. Use a profiler.
You should consider several unrelated points.
When you can go without locks, you should go without them, of course. And when you go for multithreading, prefer immutable objects.
On the other side, immutable objects
strain memory
are considered "anti-OOP"
may be incorrectly consumed by client code (because people are not used working with them)
Your second approach still requires some concurrency handling strategy, because several threads may set infos with different starting assumptions.
I am not sure that reference assignment is atomic. If it were, why does CLR have Interlocked.Exchange<T>? Thanks to Henk Holterman for pointing this out.