我正在获取XML提要并将其解析为我的MQ服务器,然后我有一个服务,它监听MQ服务器并读取其所有消息。
我有一个foreach循环,每次迭代都会打开一个新的线程,以便使解析更快,因为MQ中有大约500条消息(意味着有500个XML)
foreach (System.Messaging.Message m in msgs)
{
byte[] bytes = new byte[m.BodyStream.Length];
m.BodyStream.Read(bytes, 0, (int)m.BodyStream.Length);
System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding();
ParserClass tst = new ParserClass(ascii.GetString(bytes, 0, (int)m.BodyStream.Length));
new Thread( new ThreadStart(tst.ProcessXML)).Start();
}
在ParserClass中我有这段代码:
private static object thLockMe = new object();
public string xmlString { get; set; }
public ParserClass(string xmlStringObj)
{
this.xmlString = xmlStringObj;
}
public void ProcessXML()
{
lock (thLockMe)
{
XDocument reader = XDocument.Parse(xmlString);
//Some more code...
}
}
问题是,当我用1个线程运行这个foreach循环时,它工作得很完美,但很慢。
当我使用超过1个线程运行它时,我收到错误“对象引用未设置为对象的实例”。
我猜我的锁定有问题,因为我对线程不是很有经验。
我有点绝望,希望你能帮忙!
干杯!
答案 0 :(得分:4)
我注意到你正在运行一堆线程,其整个代码都包含在lock
语句中。你也可以用这种方式按顺序运行方法,因为你没有得到任何并行性。
答案 1 :(得分:3)
由于您在循环的每次迭代中创建一个新的ParserClass
实例,并且每次迭代都创建并启动一个新线程,因此您不需要在ParseXML
方法中锁定。
您锁定的对象当前为static
,因此它不是实例绑定的,这意味着,一旦一个线程进入您的ParseXML
方法,其他任何人都无法执行任何操作,直到第一个完成了。
您没有在线程中的Parser类中共享任何数据(来自我能看到的代码),因此您不需要在ParseXML
函数内部锁定。
如果您正在使用线程之间共享的数据,那么您应该有一个锁。
如果你要使用大量的线程,那么你最好使用ThreadPool,从你的池中获取一个有限的(可能是4个),为它们分配一些工作,并为接下来的4个任务回收它们。
创建线程是一项昂贵的操作,需要调用OS内核,因此您不希望这样做500次。这太昂贵了。此外,Windows中线程堆栈的最小保留内存为1MB,因此对于您的线程,仅为堆栈空间500MB。
最佳线程数应该等于机器中的核心数,但是因为大多数情况下这不是真的,你可以做两倍或三倍,但是你最好使用一个线程池,在那里你回收线程,而不是始终创建新线程。
答案 2 :(得分:2)
尽管这可能无法解决您的问题,但您应该使用ThreadPool,而不是创建500个并发线程,而ThreadPool以更有效的方式管理线程:
foreach (System.Messaging.Message m in msgs)
{
byte[] bytes = new byte[m.BodyStream.Length];
m.BodyStream.Read(bytes, 0, (int)m.BodyStream.Length);
System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding();
ParserClass tst = new ParserClass(ascii.GetString(bytes, 0, (int)m.BodyStream.Length));
ThreadPool.QueueUserWorkItem(x => tst.ProcessXML());
}
并确保它们尽可能同时运行更改ParserClass
中的代码(假设您确实拥有线程之间共享的资源 - 如果您没有,则不必完全锁定):
private static object thLockMe = new object();
public string XmlString { get; set; }
public ParserClass(string xmlString)
{
XmlString = xmlString;
}
public void ProcessXML()
{
XDocument reader = XDocument.Parse(xmlString);
// some more code which doesn't need to access the shared resource
lock (thLockMe)
{
// the necessary code to access the shared resource (and only that)
}
// more code
}
关于你的实际问题:
不是用相同的参数多次调用OddService.InsertEvent(...)
(远程调用和副作用的方法......)你应该调用它一次,将结果存储在变量中并对其进行所有后续操作变量。这样你也可以方便地检查它是否不是那种有时返回null的精确方法(当同时访问时?)。
修改强>
如果您将所有来电OddService.*
放入lock
区块,是否有效?