我有一个已经开始使用的应用程序,看起来我需要重新考虑一些事情。目前,该应用程序是winform应用程序。无论如何,我允许用户输入他们想要运行的线程数。我还允许用户为每个线程分配要处理的记录数。我所做的是遍历线程数变量,并相应地创建线程。我没有在线程上执行任何锁定(并且不确定是否需要)。我是线程的新手,可能遇到多核的问题。我需要一些建议,我如何才能让它表现得更好。
在创建线程之前,会从我的数据库中拉出一些记录进行处理。该列表对象被发送到线程并遍历。一旦到达循环末尾,线程就会调用数据函数来拉取一些新记录,替换列表中的旧记录。这种情况会一直持续下去,直到没有更多的记录。下面是我的代码:
private void CreateThreads()
{
_startTime = DateTime.Now;
var totalThreads = 0;
var totalRecords = 0;
progressThreadsCreated.Maximum = _threadCount;
progressThreadsCreated.Step = 1;
LabelThreadsCreated.Text = "0 / " + _threadCount.ToString();
this.Update();
for(var i = 1; i <= _threadCount; i++)
{
LabelThreadsCreated.Text = i + " / " + _threadCount;
progressThreadsCreated.Value = i;
var adapter = new Dystopia.DataAdapter();
var records = adapter.FindAllWithLocking(_recordsPerThread,_validationId,_validationDateTime);
if(records != null && records.Count > 0)
{
totalThreads += 1;
LabelTotalProcesses.Text = "Total Processes Created: " + totalThreads.ToString();
var paramss = new ArrayList { i, records };
var thread = new Thread(new ParameterizedThreadStart(ThreadWorker));
thread.Start(paramss);
}
this.Update();
}
}
private void ThreadWorker(object paramList)
{
try
{
var parms = (ArrayList) paramList;
var stopThread = false;
var threadCount = (int) parms[0];
var records = (List<Candidates>) parms[1];
var runOnce = false;
var adapter = new Dystopia.DataAdapter();
var lastCount = records.Count;
var runningCount = 0;
while (_stopThreads == false)
{
if (!runOnce)
{
CreateProgressArea(threadCount, records.Count);
}
else
{
ResetProgressBarMethod(threadCount, records.Count);
}
runOnce = true;
var counter = 0;
if (records.Count > 0)
{
foreach (var record in records)
{
counter += 1;
runningCount += 1;
_totalRecords += 1;
var rec = record;
var proc = new ProcRecords();
proc.Validate(ref rec);
adapter.Update(rec);
UpdateProgressBarMethod(threadCount, counter, emails.Count, runningCount);
if (_stopThreads)
{
break;
}
}
UpdateProgressBarMethod(threadCount, -1, lastCount, runningCount);
if (!_noRecordsInPool)
{
records = adapter.FindAllWithLocking(_recordsPerThread, _validationId, _validationDateTime);
if (records == null || records.Count <= 0)
{
_noRecordsInPool = true;
break;
}
else
{
lastCount = records.Count;
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}发布于 2010-08-16 09:45:30
I允许用户输入他们想要运行的线程数。我还允许用户为每个线程分配要处理的记录数。
这并不是你真正想要暴露给用户的东西。他们应该放什么?他们如何确定什么是最好的?这是最好的实现细节留给你,或者更好,CLR或其他库。
我没有在线程上执行任何锁定(并且不确定是否需要)。
你在多线程方面遇到的大多数问题都来自共享状态。具体地说,在您的ThreadWorker方法中,您似乎引用了以下共享数据:_stopThreads、_totalRecords、_noRecordsInPool、_recordsPerThread、_validationId和_validationDateTime。
然而,仅仅因为这些数据是共享的,并不意味着你会有问题。这完全取决于谁来读和写它们。例如,我认为_recordsPerThread最初只编写一次,然后由所有线程读取,这是很好的。但是,_totalRecords是由每个线程读取和写入的。在这里,您可能会遇到线程问题,因为_totalRecords += 1;由非原子的先读后写组成。换句话说,您可以让两个线程读取_totalRecords的值(假设它们都读取值5),然后递增其副本,然后将其写回。它们都会写回值6,现在这个值是不正确的,因为它应该是7。这是一个典型的race condition。对于这种特殊情况,您可以使用Interlocked.Increment自动更新该字段。
通常,要在C#中的线程之间进行同步,您可以使用System.Threading名称空间中的类,例如Mutex、Semaphore,以及可能最常见的Monitor (相当于lock),它一次只允许一个线程执行特定的代码部分。用于同步的机制完全取决于您的性能要求。例如,如果您在ThreadWorker的主体周围抛出一个lock,那么您将通过有效地序列化工作来破坏您通过多线程获得的任何性能增益。安全,但速度慢:(另一方面,如果您使用Interlocked.Increment,并在必要时明智地添加其他同步,您将保持您的性能,您的应用程序将是正确的:)
一旦你让你的worker方法成为线程安全的,你就应该使用一些其他的机制来管理你的线程。这里提到了ThreadPool,您还可以使用Task Parallel Library,它在ThreadPool上进行抽象,并智能地确定和缩放要使用的线程数。这样,您就可以减轻用户的负担,让他们确定应该运行的线程的神奇数量。
发布于 2010-08-16 09:27:39
您可以做一些简单的事情来提高性能,那就是使用ThreadPool来管理线程创建。这允许OS分配一组支付线程创建惩罚的线程,一次而不是多次。
如果您决定迁移到.NET 4.0,Tasks将是另一种选择。
发布于 2010-08-16 09:27:35
最明显的答案就是问你为什么首先需要线程?哪里有分析和基准测试表明使用线程将是一种优势?
如何确保非gui线程不与gui交互?如何确保没有两个线程以不安全的方式与相同的变量或数据结构交互?即使您意识到确实需要使用锁定,如何确保锁定不会导致每个线程顺序处理其工作负载,从而消除多线程可能提供的任何优势?
https://stackoverflow.com/questions/3489979
复制相似问题