我开始编写我的第一个并行应用程序。此分区程序将从数据源一次枚举IDataReader
个chunkSize
个private object _Lock = new object();
public IEnumerator GetEnumerator()
{
var infoSource = myInforSource.GetEnumerator();
//Will this cause a deadlock if two threads
lock (_Lock) //use the enumator at the same time?
{
while (infoSource.MoveNext())
{
yield return infoSource.Current;
}
}
}
个记录。
TLDR;版本
protected class DataSourcePartitioner<object[]> : System.Collections.Concurrent.Partitioner<object[]>
{
private readonly System.Data.IDataReader _Input;
private readonly int _ChunkSize;
public DataSourcePartitioner(System.Data.IDataReader input, int chunkSize = 10000)
: base()
{
if (chunkSize < 1)
throw new ArgumentOutOfRangeException("chunkSize");
_Input = input;
_ChunkSize = chunkSize;
}
public override bool SupportsDynamicPartitions { get { return true; } }
public override IList<IEnumerator<object[]>> GetPartitions(int partitionCount)
{
var dynamicPartitions = GetDynamicPartitions();
var partitions =
new IEnumerator<object[]>[partitionCount];
for (int i = 0; i < partitionCount; i++)
{
partitions[i] = dynamicPartitions.GetEnumerator();
}
return partitions;
}
public override IEnumerable<object[]> GetDynamicPartitions()
{
return new ListDynamicPartitions(_Input, _ChunkSize);
}
private class ListDynamicPartitions : IEnumerable<object[]>
{
private System.Data.IDataReader _Input;
int _ChunkSize;
private object _ChunkLock = new object();
public ListDynamicPartitions(System.Data.IDataReader input, int chunkSize)
{
_Input = input;
_ChunkSize = chunkSize;
}
public IEnumerator<object[]> GetEnumerator()
{
while (true)
{
List<object[]> chunk = new List<object[]>(_ChunkSize);
lock(_Input)
{
for (int i = 0; i < _ChunkSize; ++i)
{
if (!_Input.Read())
break;
var values = new object[_Input.FieldCount];
_Input.GetValues(values);
chunk.Add(values);
}
if (chunk.Count == 0)
yield break;
}
var chunkEnumerator = chunk.GetEnumerator();
lock(_ChunkLock) //Will this cause a deadlock?
{
while (chunkEnumerator.MoveNext())
{
yield return chunkEnumerator.Current;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<object[]>)this).GetEnumerator();
}
}
}
完整代码
IEnumerable
我希望它传回的_ChunkLock
对象是线程安全的(MSDN example所以我假设PLINQ而TPL可能需要它)将锁定在yeld return
附近的底部帮助提供线程安全还是会导致死锁?从文档中我无法判断锁是否会在{{1}}上发布。
此外,如果.net的内置功能可以做我想做的事情,我宁愿使用它。如果你发现代码有任何其他问题我会很感激。
答案 0 :(得分:1)
我写了一个测试框架,它没有死锁,但第二个线程永远不会得到数据。
static void Main()
{
En en = new En();
Task.Factory.StartNew(() =>
{
foreach (int i in en)
{
Thread.Sleep(100);
Console.WriteLine("A:" + i.ToString());
}
});
Task.Factory.StartNew(() =>
{
foreach (int i in en)
{
Thread.Sleep(10);
Console.WriteLine("B:" +i.ToString());
}
});
Console.ReadLine();
}
public class En : IEnumerable
{
object _lock = new object();
static int i = 0;
public IEnumerator GetEnumerator()
{
lock (_lock)
{
while (true)
{
if (i < 10)
yield return i++;
else
yield break;
}
}
}
}
返回
A:0
A:1
A:2
A:3
A:4
A:5
A:6
A:7
A:8
A:9
以下是GetEnumerator
的更新版本,应该正常运行。
public IEnumerator<object[]> GetEnumerator()
{
while (true)
{
List<object[]> chunk = new List<object[]>(_ChunkSize);
_ChunkPos = 0;
lock(_Input)
{
for (int i = 0; i < _ChunkSize; ++i)
{
if (!_Input.Read())
break;
var values = new object[_Input.FieldCount];
_Input.GetValues(values);
chunk.Add(values);
}
if (chunk.Count == 0)
yield break;
}
var chunkEnumerator = chunk.GetEnumerator();
while (true)
{
object[] retVal;
lock (_ChunkLock)
{
if (chunkEnumerator.MoveNext())
{
retVal = chunkEnumerator.Current;
}
else
break; //break out of chunk while loop.
}
yield return retVal;
}
}
}
答案 1 :(得分:1)
总之:也许* 。
如果您始终在foreach
循环的上下文中使用此代码,那么您不太可能遇到死锁(除非您的myInfoSource
成为可能是无限的,或者你的foreach
循环中有一些永远不会终止的代码,尽管你可能会看到减速。
更可能导致潜在(实际上,保证)死锁的原因是:
var myObject = new YourObject();
var enumerator = myObject.GetEnumerator();
// if you do this, and then forget about it...
enumerator.MoveNext();
// ...your lock will never be released
*我的答案基于你的初始代码块。