这是我的代码:
Thread _th1, _th2, _th3, _th4;
int _i;
void thMethod()
{
while(_i < 100){
Thread.Sleep((new Random()).Next(1,500));
Application.DoEvents();
Console.WriteLine(_i);
_i++;
}
}
private void button4_Click(object sender, EventArgs e)
{
_th1 = new Thread(new ThreadStart(thMethod));
_th1.Start();
_th2 = new Thread(new ThreadStart(thMethod));
_th2.Start();
_th3 = new Thread(new ThreadStart(thMethod));
_th3.Start();
_th4 = new Thread(new ThreadStart(thMethod));
_th4.Start();
}
我想做的是, 使用多线程将Console.WriteLine从0调到99, 延迟是随机的。
但是,我的代码会打印重复的数字
嗯,这就是结果。
0 1 2 2 4 五 6 7 8 9 10 10 12 13 14 14 16 16 18 19 20 20 22 22 24 24 26 26 28 28 三十 31 32 33 34 35 36 36 38 39 40 40 42 42 44 44 46 46 48 49 50 50 52 52 54 54 56 56 58 58 60 60 62 62 64 65 66 66 68 68 70 70 72 72 74 74 76 76 78 78 80 80 82 82 84 84 86 87 88 88 90 90 92 92 94 94 96 96 98 98 100 101 102
如您所见,打印出重复的数字, 它甚至没有停在99.
那么......如何更正此代码才能使其正常运行?
======================我必须改变......
void thMethod()
{
while(_i < 100){
Thread.Sleep((new Random()).Next(1,500));
Application.DoEvents();
Interlocked.Increment(ref _i);
Console.WriteLine(_i);
}
}
此?
答案 0 :(得分:3)
虽然您应该使用其他帖子中提到的Interlocked.Increment
。这不是你遇到的确切问题。
您的数字超过100的原因是因为您正在增加值的行是在Sleep之后。这会导致线程进入循环,但是当它们写出值时,值已经变为100以上。
例如t1,t2和t3进入循环并等待500ms,我是99.然后t4退出并将其增加到100,现在t1将打印100,t2将打印101,t3将打印102。
Interlocked.Increment实际上不会解决问题,因为Thread.Sleep在实际写出值时仍然会导致不一致。
要解决此问题,您需要使用thread lock 声明要锁定的变量,因为你不能锁定_i,因为它不是引用类型:
object obj = new object();
void thMethod()
{
while (_i < 100)
{
lock (obj)
{
Console.WriteLine(_i);
_i++;
}
Thread.Sleep((new Random()).Next(1, 500));
}
}
这样你就不会重复,价值将停在99.
答案 1 :(得分:1)
您需要以线程安全的方式调用Interlocked.Increment
来增加i
。
答案 2 :(得分:1)
在多线程程序中,具有多个写入器的共享资源需要某种同步机制。
在其中一个线程有机会增加它之前,有几个线程正在访问变量进行打印,例如race condition
。
要解决这个问题,您需要在关键部分增加变量:
void thMethod()
{
while (_i < 100)
{
Thread.Sleep((new Random()).Next(1, 500));
Application.DoEvents();
lock(this)
{
Console.WriteLine(_i);
_i++;
}
}
}
现在这不会停在100,所以另一个修复将是:
while (_i < 100)
{
Thread.Sleep((new Random()).Next(1, 500));
Application.DoEvents();
lock(this)
{
if (_i >= 100) break;
Console.WriteLine(_i);
_i++;
}
}
答案 3 :(得分:0)
变量_i必须在thMethod中声明,而不是在全局级别声明。
答案 4 :(得分:0)
除了使用未经探索的Monitor之外,还有两个选项
如果您只是使用其中一种Interlocked方法递增整数类型,则可以提供最佳性能。由于您还使用此变量来表示完成Interlocked.CompareExchange
,因此非常适合。这种技术用于无锁和无等待的解决方案。
while (_i < 100)
{
Thread.Sleep((new Random()).Next(1, 500));
int initI;
int modifiedI;
bool valueUpdated = false;
do
{
initI = _i;
modifiedI = initI + 1;
if (modifiedI > 100)
break;
valueUpdated = (initI == Interlocked.CompareExchange(
ref _i, modifiedI, initI) );
if (valueUpdated)
Console.WriteLine(modifiedI);
} while (!valueUpdated) ;
}
如果你想要简单,你也可以使用Parallel Class
Parallel.For(0, 100, i =>
{
Thread.Sleep((new Random()).Next(1, 500));
_i++;
Console.WriteLine("{0} | {1}" ,Thread.CurrentThread.ManagedThreadId, _i );
}
);
出于演示目的,我添加了ManagedThreadId的输出,以便您可以看到正在使用多个线程。
您应该注意到,您无法直接控制创建的线程数,而是可以使用MaxDegreeOfParallelism集合传递ParallelOption,这会影响线程数。
答案 5 :(得分:-1)
我用互锁解决了这个问题。 这是一个vb.net控制台应用程序,但很容易理解。 每个线程都有其唯一的计数器,显然输出序列没有排序
模块模块1
Dim count As Integer = 0
Sub Main()
For i = 1 To 10
Dim th As New Thread(AddressOf ThreadedIncrement)
th.Start()
Next
Console.ReadLine()
End Sub
Sub ThreadedIncrement()
Dim r As New Random
For i = 1 To 10
Thread.SpinWait(r.Next(1000, 1000000))
Dim ncount As Integer = Interlocked.Add(count, 1)
Console.WriteLine(ncount.ToString)
Next
End Sub
结束模块