C#MultiThread没有重复

时间:2012-07-30 21:53:40

标签: c# multithreading duplicates

这是我的代码:

    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);
        }
    }

此?

6 个答案:

答案 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

结束模块