简单地分配变量时是否需要使用锁?

时间:2013-07-03 18:43:13

标签: c# multithreading

当多线程我知道我需要锁定变量,如果做一些事情,如添加和项目到列表等,或者我会得到一个跨线程异常。但是在分配变量时我是否需要锁定它们?我不介意线程是否获得变量的旧实例 - 我只是不希望它出错。这是我的意思的一个例子:

    public void Run()
    {
        var thread1 = new Thread(new ThreadStart(Test));
        var thread2 = new Thread(new ThreadStart(Test));
        thread1.Start();
        thread2.Start();
    }

    private static int _test;

    private void Test()
    {
        while (true)
        {
            _test += 1;
        }
    }

3 个答案:

答案 0 :(得分:5)

如果你只是分配一个int,那么没有。但在这里,你不只是分配。你正在递增。所以你需要某种同步。

如果您想要增加,请使用Interlocked.Increment

Interlocked.Increment(ref _test);

答案 1 :(得分:0)

你必须记住线程也可能正在查看陈旧的副本,通过锁定你确保正在刷新的变量版本正在刷新

当我第一次开始编码并认为可能我不需要变量的最新副本时,我会陷入无限循环,因为我假设变量最终会更新,但如果变量被缓存则会永远不会更新

我包含了简短描述的示例,不用担心线程的启动方式,这是不相关的

private static bool _continueLoop = true;
private static readonly object _continueLoopLock = new object();

private static void StopLoop() 
{ 
    lock(_continueLoopLock) 
        _continueLoop = false;
}

private static void ThreadALoopWillGetStales()
{
    while(_continueLoop)
    {
        //do stuff
        //this is not guaranteed to end
    }
}

private static void ThreadALoopEventuallyCorrect()
{
    while(true)
    {
        bool doContinue;

        lock(_continueLoopLock)
            doContinue = _continueLoop;

        if(!doContinue)
            break;

        //do stuff
        //this will sometimes result in a stale value
        //but will eventually be correct
    }
}

private static void ThreadALoopAlwaysCorrect()
{
    while(true)
    {
        bool doContinue;

        lock(_continueLoopLock)
           if(!_continueLoop)
            break;

        //do stuff
        //this will always be correct
    }
}

private static void ThreadALoopPossibleDeadlocked()
{
     lock(_continueLoopLock)
         while(_continueLoop)
         {
             //if you only modify "_continueLoop"
             //after Acquiring "_continueLoopLock"
             //this will cause a deadlock 
         }
}

private static void StartThreadALoop()
{
    ThreadPool.QueueUserWorkItem ((o)=>{ThreadALoopWillGetStales();});
}
private static void StartEndTheLoop()
{
    ThreadPool.QueueUserWorkItem((o)=>
    {
       //do stuff
       StopLoop();
    });
}

public static void Main(string[] args)
{
    StartThreadALoop();
    StartEndTheLoop();
}

当你开始循环时,你有可能继续获得变量的陈旧副本,这就是为什么你在访问多个线程时需要某种同步

答案 2 :(得分:0)

运行代码应该给你答案......而不是while(true)for(i=1;1<1e6;i++),将结果写入屏幕并运行它。

你会发现它并不是2e6,而是1.2e6左右。所以,是的,如果你想离开2e6,你需要锁定。

不要只是假设,之后总是测试和断言。