代码明细:
// Singleton class CollectionObject
public class CollectionObject
{
private static CollectionObject instance = null;
// GetInstance() is not called from multiple threads
public static CollectionObject GetInstance()
{
if (CollectionObject.instance == null)
CollectionObject.instance = new CollectionObject();
return CollectionObject.instance;
}
// Dictionary object contains Service ID (int) as key and Service object as the value
// Dictionary is filled up during initiation, before the method call ReadServiceMatrix detailed underneath
public Dictionary<int, Service> serviceCollectionDictionary = new Dictionary<int,Service>();
public Service GetServiceByIDFromDictionary(int servID)
{
if (this.serviceCollectionDictionary.ContainsKey(servID))
return this.serviceCollectionDictionary[servID];
else
return null;
}
}
DataTable serviceMatrix = new DataTable();
// Fill serviceMatrix data table from the database
private int ReadServiceMatrix()
{
// Access the Singleton class object
CollectionObject collectionObject = CollectionObject.GetInstance();
// Parallel processing of the data table rows
Parallel.ForEach<DataRow>(serviceMatrix.AsEnumerable(), row =>
{
//Access Service ID from the Data table
string servIDStr = row["ServID"].ToString().Trim();
// Access other column details for each row of the data table
string currLocIDStr = row["CurrLocId"].ToString().Trim();
string CurrLocLoadFlagStr = row["CurrLocLoadFlag"].ToString().Trim();
string nextLocIDStr = row["NextLocId"].ToString().Trim();
string nextLocBreakFlagStr = row["NextLocBreakFlag"].ToString().Trim();
string seqStr = row["Seq"].ToString().Trim();
int servID = Int32.Parse(servIDStr);
int currLocID = Int32.Parse(currLocIDStr);
int nextLocID = Int32.Parse(nextLocIDStr);
bool nextLocBreakFlag = Int32.Parse(nextLocBreakFlagStr) > 0 ? true : false;
bool currLocBreakFlag = Int32.Parse(CurrLocLoadFlagStr) > 0 ? true : false;
int seq = Int32.Parse(seqStr);
// Method call leading to the issue (definition in Collection Object class)
// Fetch service object using the Service ID from the DB
Service service = collectionObject.GetServiceByIDFromDictionary(servID);
// Call a Service class method
service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID, currLocBreakFlag, nextLoc.SequentialID, nextLocBreakFlag, seq));
}
发生的问题是:
在上面的代码中,对于字典中的所有Service对象,不会进行后续的方法调用,从而导致进一步处理中的问题。它必须以并行模式从字典中获取Service对象
db一个字典包含所有的Ids / Service对象,但我的理解是当在Singleton类的并行模式下处理时,很少有对象被跳过导致问题。
在我的理解中,传递的服务ID和创建的服务对象是一个线程的本地,所以不应该有我面临的问题。这种问题是唯一可能的,当一个给定的方法调用一个线程替换另一个线程的服务id值时,因此两者都以Service对象结束,因此很少被跳过,这在我看来很奇怪,除非我做在这种情况下不能正确理解多线程
目前我可以使用foreach循环而不是Parallel.ForEach / Parallel.Invoke
请查看并告诉我您的观点或任何有助于我解决问题的指针
答案 0 :(得分:1)
1.实施单身人士总是考虑以多种方式使用它。始终使用多线程单例模式变体,其中之一 - 懒惰单例。使用带有System.Lazy consturctor参数的LazyThreadSafeMode的Lazy单例:
public class LazySingleton3
{
// static holder for instance, need to use lambda to enter code here
//construct since constructor private
private static readonly Lazy<LazySingleton3> _instance
= new Lazy<LazySingleton3>(() => new LazySingleton3(),
LazyThreadSafeMode.PublicationOnly);
// private to prevent direct instantiation.
private LazySingleton3()
{
}
// accessor for instance
public static LazySingleton3 Instance
{
get
{
return _instance.Value;
}
}
}
了解here
2.在并行循环体中锁定服务变量
// Method call leading to the issue (definition in Collection Object class)
// Fetch service object using the Service ID from the DB
Service service = collectionObject.GetServiceByIDFromDictionary(servID);
lock (service)
{
// Call a Service class method
service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID,
currLocBreakFlag, nextLoc.SequentialID,
nextLocBreakFlag, seq));
}
3.考虑在这里使用多线程。使用锁定代码使您的代码不像同步那样具有性能。因此,请确保多线程/并行代码为您提供优势
4.使用适当的并发集合而不是重新发明轮子 - System.Collections.Concurrent Namespace
答案 1 :(得分:0)
在我的理解中,传递了服务id并创建了服务对象 是一个线程的本地
您的理解是不正确的,如果两个线程请求相同的服务ID,则两个线程将同时处理同一个单个对象。如果您想要单独的对象,则需要在new Service()
中进行某种GetServiceByIDFromDictionary
调用,而不是现有值的字典。
由于多个线程可能使用相同的service
个对象,我认为您的问题在于service.InitLanes.Add
可能不是线程安全的事实。
最简单的解决办法就是锁定这一步
//...SNIP...
Service service = collectionObject.GetServiceByIDFromDictionary(servID);
// Call a Service class method, only let one thread do it for this specific service instance,
// other threads locking on other instances will not block, only other threads using the same instance will block
lock(service)
{
service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID, currLocBreakFlag, nextLoc.SequentialID, nextLocBreakFlag, seq));
}
}
这假定此Parallel.Foreach
是唯一同时使用collectionObject.GetServiceByIDFromDictionary
的位置。如果不是,那么可能在返回的服务上调用任何方法的任何其他位置也必须锁定service
。
但是,如果Service在您的控制之下,您可以以某种方式将service.InitLanes.Add
修改为线程安全(可能使用InitLanes
命名空间中的线程安全集合更改System.Collections.Concurrent
)比锁定更好的解决方案。