所以,我有一个自定义线程池,它接收最大数量的线程,你可以在其中排队项目。池将执行队列中的项目。问题是这个池的行为总是不同的。我创建了一个测试,它给这个池提供了X个操作,并等待池完成工作(这个等待有一个限制,但是限制足以让所有操作成功结束)。问题是,有时测试会返回“成功”响应,但大多数情况下它会超出时间限制和/或不会处理所有操作。
代码:
CustomThreadPool.cs
public class CustomThreadPool : IDisposable
{
#region Private Members
private readonly Thread m_checkThread;
#endregion
#region Public Properties
public int MaxNumberOfThreads { get; set; }
private readonly object m_lock = new object();
private int m_currentNumberOfThreads;
public int CurrentNumberOfThreads
{
get
{
lock (m_lock)
{
return m_currentNumberOfThreads;
}
}
private set
{
lock (m_lock)
{
m_currentNumberOfThreads = value;
}
}
}
public ConcurrentQueue<WorkItem> QueuedItems { get; set; }
#endregion
#region Constructor
public CustomThreadPool(int maxNumberOfThreads)
{
MaxNumberOfThreads = maxNumberOfThreads;
QueuedItems = new ConcurrentQueue<WorkItem>();
m_checkThread = new Thread(CheckThread);
m_checkThread.Start();
}
#endregion
#region Public Methods
public void QueueItem(object argument, Action<WorkItem> method, string token = "")
{
QueuedItems.Enqueue(new WorkItem { Argument = argument, Method = method, Token = token });
}
public List<WorkItem> Stop()
{
m_checkThread.Abort();
List<WorkItem> result = new List<WorkItem>();
while (QueuedItems.Count > 0)
{
WorkItem wi;
QueuedItems.TryDequeue(out wi);
if (wi != null)
result.Add(wi);
}
CurrentNumberOfThreads = 0;
return result;
}
#endregion
#region Private Methods
// ReSharper disable once FunctionNeverReturns
private void CheckThread()
{
while (true)
{
if (CurrentNumberOfThreads >= MaxNumberOfThreads || QueuedItems.Count == 0)
{
Thread.Yield();
}
int availableThreads = MaxNumberOfThreads - CurrentNumberOfThreads;
List<WorkItem> toBeProcessed = new List<WorkItem>();
for (var i = 0; i < availableThreads; i++)
{
WorkItem wi;
QueuedItems.TryDequeue(out wi);
if (wi != null)
{
toBeProcessed.Add(wi);
}
}
foreach (WorkItem item in toBeProcessed)
{
CurrentNumberOfThreads++;
item.ExecutingThread = new Thread(ProcessItem);
item.ExecutingThread.Start(item);
}
Thread.Sleep(50);
}
}
private void ProcessItem(object wi)
{
WorkItem item = (WorkItem)wi;
item.Method.Invoke(item);
CurrentNumberOfThreads--;
item.ExecutingThread.Abort();
}
#endregion
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposing)
{
m_checkThread.Abort();
}
}
}
测试:
private List<int> EndedOperations = new List<int>();
private List<int> StartedOperations = new List<int>();
public void CheckThreadPool()
{
int workTime = 100;
int numberOfOperations = 100;
int numberOfThreads = 10;
int cycles = numberOfOperations / numberOfThreads;
int totalTime = workTime * 2 * cycles;
Stopwatch sw = new Stopwatch();
sw.Start();
CustomThreadPool pool = new CustomThreadPool(numberOfThreads);
for (int i = 0; i < numberOfOperations; i++)
{
pool.QueueItem(new WorkInfo(i, workTime), DoWork);
}
Thread.Sleep(workTime);
bool queueEmpty = false, operationsDone = false;
while (pool.CurrentNumberOfThreads > 0 && sw.ElapsedMilliseconds < totalTime)
{
if (pool.QueuedItems.Count == 0 && !queueEmpty)
{
queueEmpty = true;
Debug.WriteLine("Queue emptied at: {0}, operations left: {1}", sw.ElapsedMilliseconds, numberOfOperations - EndedOperations.Count);
}
if (EndedOperations.Count == numberOfOperations && !operationsDone)
{
operationsDone = true;
Debug.WriteLine("Operations done at: {0}, number of threads: {1}", sw.ElapsedMilliseconds, pool.CurrentNumberOfThreads);
}
Thread.Yield();
}
sw.Stop();
pool.Dispose();
Thread.Sleep(workTime);
Debug.WriteLine("Test ended with {0} unprocessed operations", numberOfOperations - EndedOperations.Count);
for (int i = 0; i < numberOfOperations; i++)
{
if (!EndedOperations.Contains(i))
Debug.WriteLine("Operation {0} was not fully processed", i);
if (!StartedOperations.Contains(i))
Debug.WriteLine("Operation {0} has never started", i);
}
Assert.IsTrue(sw.ElapsedMilliseconds < totalTime,
string.Format(@"The pool did not stop in useful time.
Remaining threads : {0}
Remaining queued items : {1}
Remaining operations: {2}",
pool.CurrentNumberOfThreads, pool.QueuedItems.Count, numberOfOperations - EndedOperations.Count));
Assert.IsTrue(pool.QueuedItems.Count == 0,
string.Format(@"Not all items were processed.
Remaining : {0}
Processing time : {1}",
pool.QueuedItems.Count, sw.ElapsedMilliseconds));
}
private void DoWork(WorkItem wi)
{
WorkInfo info = (WorkInfo)wi.Argument;
try
{
StartedOperations.Add(info.Id);
Thread.Sleep(info.TestTime);
EndedOperations.Add(info.Id);
}
catch(Exception ex)
{
Debug.WriteLine("id: {0}, ex: {1}", info.Id, ex.Message);
}
}
我假设问题可能来自共享资源,但我无法弄清楚哪一个。
谢谢。
答案 0 :(得分:1)
即使你在lock
的赋值上放了一个CurrentNumberOfThreads
,这还不够,因为你的递增和递减操作不是原子的。
您应该使用Interlocked
类提供的原子操作:
替换
CurrentNumberOfThreads++;
使用
System.Threading.Interlocked.Increment(ref m_CurrentNumberOfThreads);
和
CurrentNumberOfThreads--;
带
System.Threading.Interlocked.Decrement(ref m_CurrentNumberOfThreads);
另外需要注意的是:永远不要abort
这样的线程。尽量让某些方式让他们得到通知并优雅地退出。