好笑,我打算测试墓碑和并发性,在我简单的设置中,我想到了什么,真的值得麻烦。但是现在,在设置了一些愚蠢的测试类45分钟之后,我遇到了第一个我不理解的错误。
似乎我需要在列表,锁和线程中多练习一下。 有谁知道为什么会抛出非法的操作异常(见附件)。
对于更喜欢F5体验的人来说,这是完整的解决方案(300kB)
http://www.filesavr.com/TXXXFVE40GTJK43
不要打开视图,它们可能会导致VS2010崩溃。而且你需要WP7工具,对不起,即使我很确定这个例子在纯C#上也可以工作(不工作)。
[编辑]
我更新链接,现在它正在工作(使用旧代码)
我发现了第一个错误,感谢评论。
这有效:
private void IncOneWithLock()
{
lock (CounterListOne)
{
IncListOne();
}
}
private void IncListOne()
{
if (CounterListOne == null)
{
Log("CounterListOne == null");
return;
}
var c = 0;
var oldList = CounterListOne.ToList();
foreach (var i in oldList)
{
CounterListOne[c++] = i + 1;
Thread.Sleep(Next(80*DelayFactor, 150*DelayFactor));
}
}
将继续测试逻辑删除的内容,并在以后的帖子中发布可能的问题。迭代时更改列表 - 你好新手的错误: - )
[/ EDIT]
对于你的常规,这个功能发生在这个功能中,并且它的操作无效:
private void IncOneWithLock()
{
if (CounterListOne == null)
{
Log("CounterListOne == null");
return;
}
lock (this)
{
var c = 0;
foreach (var i in CounterListOne)
{
CounterListOne[c++] = i + 1;
Thread.Sleep(Next(80 * DelayFactor, 150 * DelayFactor));
}
}
}
以下是测试类的完整来源:
public class CounterClass : TestBase
{
private DispatcherTimer _dT;
public int CounterA { get; set; }
public ObservableCollection<int> CounterListOne { get; set; }
public List<int> CounterListTwo { get; set; }
public List<int> CounterListThree { get; set; }
private const int DelayFactor = 10;
public CounterClass()
{
CounterListOne = new ObservableCollection<int>();
CounterListTwo = new List<int>();
CounterListThree = new List<int>();
InitCounterLists();
StartBackgroundLogger();
}
public void LogLists()
{
lock (this)
//lock (CounterListTwo)
// lock (CounterListThree)
{
Log("====================================================");
Log("CounterListOne " + String.Join("-", CounterListOne.Select(x => x.ToString()).ToArray()));
Log("CounterListTwo " + String.Join("-", CounterListTwo.Select(x => x.ToString()).ToArray()));
Log("CounterListThree " + String.Join("-", CounterListThree.Select(x => x.ToString()).ToArray()));
Log("====================================================");
}
}
public void RunTests()
{
Log("MultiIncWithoutLocks");
//MultiIncWithoutLocks();
Log("MultiIncWithLocks");
MultiIncWithLocks();
}
public void MultiIncWithoutLocks()
{
ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithoutLock());
}
public void MultiIncWithLocks()
{
ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
ThreadPool.QueueUserWorkItem(x => IncOneWithLock());
}
private void IncOneWithoutLock()
{
var c = 0;
foreach (var i in CounterListOne)
{
CounterListOne[c++] = i+1;
Thread.Sleep(Next(80 * DelayFactor, 150 * DelayFactor));
}
}
private void IncOneWithLock()
{
if (CounterListOne == null)
{
Log("CounterListOne == null");
return;
}
lock (this)
{
var c = 0;
foreach (var i in CounterListOne)
{
CounterListOne[c++] = i + 1;
Thread.Sleep(Next(80 * DelayFactor, 150 * DelayFactor));
}
}
}
private void InitCounterLists()
{
InitCounterOne();
InitCounterTwo();
InitCounterThree();
}
private void InitCounterOne()
{
for (int i = 0; i < Next(1, 5); i++)
{
CounterListOne.Add(0);
}
}
private void InitCounterTwo()
{
for (int i = 0; i < Next(1, 5); i++)
{
CounterListTwo.Add(0);
}
}
private void InitCounterThree()
{
for (int i = 0; i < Next(1, 5); i++)
{
CounterListThree.Add(0);
}
}
private void StartBackgroundLogger()
{
_dT = new DispatcherTimer();
_dT.Tick += (a,b) => LogLists();
_dT.Interval = new TimeSpan(0,0,0,3);
_dT.Start();
}
}
答案 0 :(得分:3)
您应该提供更详细的例外说明。它与foreach
和CounterListOne相关联:您在迭代其值时对其进行更改,结果为InvalidOperationException
。
答案 1 :(得分:0)
您正在使用可观察的集合来绑定您的视图吗?
这里发生的事情是,通过更新observablecollection中的值,您可能会尝试通过非UI线程更新UI,从而导致您的异常。您是否使用Dispatcher.BeginInvoke更新值而不是它将起作用
Dispatcher.BeginInvoke( () => { // This code is on the UI thread. });
不幸的是,这将排除或破坏部分测试,因为它会将所有值更新活动卸载回UI。解决此问题的方法是创建自定义Iobservable
元素。您可以根据需要存储和更新该对象中的值,并在需要时手动调用Updated
事件(通过Dispatcher.BeginInvoke
)。这至少将值的更新完全放在单独的线程上。但由于Silverlight可视化的排列方式,您永远无法从单独的线程