C# 每个竞争条件的并行性
在parallel.ForEach循环中,我周期性地进入我认为是竞争条件的状态。我这样说是因为它总是挂在代码的这一部分上C# 每个竞争条件的并行性,c#,parallel.foreach,C#,Parallel.foreach,在parallel.ForEach循环中,我周期性地进入我认为是竞争条件的状态。我这样说是因为它总是挂在代码的这一部分上 try { Parallel.ForEach(Directory.EnumerateFiles(directory, "*.tracex", SearchOption.TopDirectoryOnly), _po, (path, ls) => { DebugFile file; if (filter
try
{
Parallel.ForEach(Directory.EnumerateFiles(directory, "*.tracex", SearchOption.TopDirectoryOnly), _po, (path, ls) =>
{
DebugFile file;
if (filterDate)
{
if (filterUser)
{
file = new DebugFile(path, startTime, endTime, user);
}
else file = new DebugFile(path, startTime, endTime);
}
else if (filterUser)
{
file = new DebugFile(path, user);
}
else file = new DebugFile(path);
if (!file.IsFiltered())
{
_files.Add(file);
}
Interlocked.Increment(ref _loadCount); // increment how many we've checked
if (_po.CancellationToken.IsCancellationRequested)
{
ls.Break();
}
});
}
catch (OperationCanceledException oce)
{
Debug.WriteLine(oce.ToString());
}
在my_files对象中,我在调用Add方法时处理锁定
public virtual void Add(T item)
{
_lock.EnterWriteLock();
try
{
_bindingList.Add(item);
}
finally
{
_lock.ExitWriteLock();
}
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _bindingList.Count - 1));
}
知道我做错了什么吗?它不是每次都挂,只是偶尔挂。而且,至少对我来说,它不会在我第一次调用代码时发生。只有当我调用它一次,然后再调用它时才会发生,通常是第二次或第三次
谢谢
更新
我意识到我正在使用一个定制的任务调度器。当我移除它时,我再也看不到挂起。我这样做是为了可以自定义运行的线程数。我的想法是,由于我主要是通过网络读取文件,IO会减慢速度,这样我就可以一次运行更多任务。下面是我如何构建调度器的:
public class TaskSchedulerForSlowIO : TaskScheduler
{
/// <summary>
/// maximum number of tasks to run concurrently
/// </summary>
private int _maxConcurrencyLevel;
/// <summary>
/// lock for reading tasks array
/// </summary>
private ReaderWriterLockSlim _listLock = new ReaderWriterLockSlim();
/// <summary>
/// list of tasks running
/// </summary>
private LinkedList<Task> _tasks = new LinkedList<Task>();
/// <summary>
/// Default constructor - This will increase threadpool limits if necessary
/// </summary>
public TaskSchedulerForSlowIO()
: base()
{
_maxConcurrencyLevel = Environment.ProcessorCount * 10;
int workerThreads, ioThreads, minimumConcurrency;
minimumConcurrency = Environment.ProcessorCount * 2;
ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
if (workerThreads < _maxConcurrencyLevel)
{
if (ioThreads < _maxConcurrencyLevel)
{
ioThreads = _maxConcurrencyLevel;
}
ThreadPool.SetMaxThreads(_maxConcurrencyLevel, ioThreads);
}
ThreadPool.GetMinThreads(out workerThreads, out ioThreads);
if (workerThreads < minimumConcurrency)
{
if (ioThreads < minimumConcurrency)
{
ioThreads = minimumConcurrency;
}
ThreadPool.SetMinThreads(minimumConcurrency, ioThreads);
}
}
/// <summary>
/// Implementing TaskScheduler
/// </summary>
public override int MaximumConcurrencyLevel
{
get
{
return _maxConcurrencyLevel;
}
}
/// <summary>
/// Scheduler Implementation
/// </summary>
/// <returns>ScheduledTasks</returns>
protected override IEnumerable<Task> GetScheduledTasks()
{
Task[] tasks;
_listLock.EnterReadLock();
try
{
tasks = _tasks.ToArray();
}
finally
{
_listLock.ExitReadLock();
}
return tasks;
}
/// <summary>
/// Queues the specified task
/// </summary>
/// <param name="task">Task to queue</param>
protected override void QueueTask(Task task)
{
int count;
_listLock.EnterReadLock();
try
{
_tasks.AddLast(task);
count = _tasks.Count;
}
finally
{
_listLock.ExitReadLock();
}
if (count <= _maxConcurrencyLevel)
{
ThreadPool.UnsafeQueueUserWorkItem(ProcessTask, task);
}
}
/// <summary>
/// Scheduler Implementation
/// </summary>
/// <param name="task">Task to remove</param>
/// <returns>Success</returns>
protected override bool TryDequeue(Task task)
{
_listLock.EnterWriteLock();
try
{
return _tasks.Remove(task);
}
finally
{
_listLock.ExitWriteLock();
}
}
/// <summary>
/// Scheduled Implementation
/// </summary>
/// <param name="task">Task to execute</param>
/// <param name="taskWasPreviouslyQueued">Was the task previously queued</param>
/// <returns></returns>
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
//We're not going to inline slow IO
return false;
}
void ProcessTask(object o)
{
try
{
Task t = o as Task;
if (t != null)
{
if (base.TryExecuteTask(t))
{
if(!(t.IsCanceled || t.IsFaulted)) t.Wait();
TryDequeue(t);
}
}
}
catch(AggregateException a)
{
var e = a.Flatten();
foreach (Exception ex in e.InnerExceptions)
{
Debug.WriteLine(ex.ToString());
}
}
}
}
公共类TaskSchedulerForSlowIO:TaskScheduler
{
///
///要同时运行的最大任务数
///
私有int_最大并发级别;
///
///读取任务数组的锁
///
私有ReaderWriterLockSlim _listLock=new ReaderWriterLockSlim();
///
///正在运行的任务列表
///
私有LinkedList_tasks=新建LinkedList();
///
///默认构造函数-这将在必要时增加线程池限制
///
公共任务调度程序forslowio()
:base()
{
_maxConcurrencyLevel=Environment.ProcessorCount*10;
int workerThreads、ioThreads、minimumConcurrency;
minimumConcurrency=Environment.ProcessorCount*2;
GetMaxThreads(out-workerThreads,out-ioThreads);
if(workerThreads<_maxConcurrencyLevel)
{
如果(ioThreads<_maxConcurrencyLevel)
{
ioThreads=\u最大并发级别;
}
SetMaxThreads(_maxConcurrencyLevel,ioThreads);
}
GetMinThreads(out-workerThreads,out-ioThreads);
if(workerThreads<最小并发)
{
if(ioThreads 如果(count),可能有很多原因。
比如说
1) 从代码上看,filterUser
,filterDate
,IsFiltered()
…的起源以及突变特征不清楚,这可能会导致问题
2) 一般来说,代码是不可伸缩的。避免并行访问(读取)文件,因为IO设备(我想在您的情况下是硬盘)不是并行读取设备,并且您很可能会在简单串行处理的情况下获得更差的性能
建议:将线程亲和力设置为仅2个线程/核心(我再次假定您有更多),然后调试,看看会发生什么。最有可能的情况是,您会遇到冲突。我首先要摆脱Add
方法中的try
/finally
。捕获所有异常处理会使调试变得非常困难。这样做,然后看看是否会抛出任何东西。@Enigmativitytry/finally
不会mean Catch all exception只是为了确保执行了\u lock.ExitWriteLock();
。griztown,当您将方法替换为lock(aSharedObj)\u bindingList.Add(item);
?什么是\u files
?类型的对象具有Add()
您显示的方法?您应该注意,如果您正在写入对象,则需要保护对该对象的所有访问;例如,\u bindingList.Count
不是线程安全的,并且必须位于锁内。由于缺少完整的代码示例,您可能还存在其他线程错误。如果您需要具体答案。_files是我创建的一个类,它是一个可排序的绑定列表。因此,_files对象的基础_bindinglist属性。因此,听起来我需要在parallel.ForEach循环中执行任何操作之前锁定_文件?您的帖子并不是问题的答案。建议:不要尝试回答无法回答的问题。谢谢s Tigran.filterUser和filterDate是布尔值,它们告诉我们是否要根据关联的用户或创建文件的日期/时间筛选文件。IsFiltered是DebugFile类上的一个标志,用于设置是否应筛选文件。我将测试其他建议。我将线程设置为8(我有4个核)现在它每次都挂起。但我还是不明白为什么。你知道谁来调试这个吗?我找到了问题的根源,一个自定义任务调度程序。我在上面添加了代码。如果有什么问题,请告诉我。为什么要编写\u maxConcurrencyLevel=Environment.ProcessorCount*10;
?你希望每个核心运行10个线程吗?