为什么 lock 没有起作用

.Net技术 码拜 8年前 (2016-05-30) 1278次浏览
// 点击按钮后,执行输出如下:
xxxxxxxxxxxx
aaaaaaaaaaaa
xxxxxxxxxxxx
aaaaaaaaaaaa
// 将第二行“lt.testc(textBox1)”注释上,连续点击两次按钮,输出如下:
xxxxxxxxxxxx
xxxxxxxxxxxx
aaaaaaaaaaaa
aaaaaaaaaaaa
看起来好像lock没有起作用,为什么?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private LockTest lt = new LockTest();
private void button1_Click(object sender, EventArgs e)
{
lt.testc(textBox1);
lt.testc(textBox1);   // 把此行注释上,连续点两次button1按钮,发现lock没有锁住
}
}
public class LockTest
{
private static object locker = new object();
public void testc(TextBox tb)
{
lock (locker)
{
tb.Invoke(new Action(() => tb.AppendText(“xxxxxxxxxxxx\r\n”)));
var sw = new Stopwatch();
sw.Start();
while (sw.ElapsedMilliseconds < 2000)
{
Thread.Sleep(1);
Application.DoEvents();
}
tb.Invoke(new Action(() => tb.AppendText(“aaaaaaaaaaaa\r\n”)));
}
}
}
解决方案

20

lock对不同线程来的请求会互斥。但是,同一个线程下”嵌套”的lock,并不互锁。
例如:

void A()
{
    lock (locker)
    {
        lock(locker)
        {
            // 可以运行到这里。
        }
    }
}

而 Application.DoEvents会运行UI的消息循环:

public void testc(TextBox tb)
{
    // ...
    Application.DoEvents();
    // ...
}

UI的消息循环的结果就是 button1_Click有机会得到运行
因此,出现了在UI线程下的button1_Click嵌套运行,而按照lock的分析,同一个线程下的lock是可以直接获得的。

5

引用:
Quote: 引用:

lock对不同线程来的请求会互斥。但是,同一个线程下”嵌套”的lock,并不互锁。

“同一个线程下”嵌套”的lock”怎么理解?为什么 lock 没有起作用
button1_Click方法中的两个lt.testc(textBox1) 应该是在同一个线程中的,假如同线程的不互锁,为什么第二个方法要等第一个方法执行完才能执行呢?

原因是你这里写的while (sw.ElapsedMilliseconds < 2000) 两秒呀。只要连续的两次操作没有超过2秒就不会执行tb.Invoke(new Action(() => tb.AppendText(“aaaaaaaaaaaa\r\n”)));

10

lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("xxxxxxxxxxxx\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(1);
                    Application.DoEvents();
                }
                tb.Invoke(new Action(() => tb.AppendText("aaaaaaaaaaaa\r\n")));
            }

下次发的时候。带上标签,否则很难看。
本人是这么理解的。不知道说的对不对,假如不注释,很容易理解。就是while函数阻止了第二个方法的运行。这个你应该可以理解。lock关键字根本就没用。去掉lock关键字也是一样的效果。
注释掉点两次的话。lock本人觉得还是起作用的,这里关键是这个Application.Doevent()函数。看过msdn的解释。说实在的很不形象,本人个人是这么理解的。这个函数执行的时候,就算有长方法使得界面假死,也可以允许其他控件或信息进行操作。而已经在运行的长时间程序会被暂停。下面是本人的测试例子:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private LockTest lt = new LockTest();
        
        private void button1_Click_1(object sender, EventArgs e)
        {
            lt.testc(textBox1);
            
        }
        private void button2_Click(object sender, EventArgs e)
        {
            lt.testb(textBox1);
        }
    }
    public class LockTest
    {
        private static object locker = new object();
        public void testc(TextBox tb)
        {
            lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("aaaa\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(1);
                    Application.DoEvents();
                }
                tb.Invoke(new Action(() => tb.AppendText("axaxax\r\n")));
            }
        }
        public void testb(TextBox tb)
        {
            lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("bbbb\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(1);
                    Application.DoEvents();
                }
                tb.Invoke(new Action(() => tb.AppendText("bxbxbx\r\n")));
            }
        }
    }

为了区分两个长时间方法,本人用了两个方法,被一个锁lock。点击第一个运行第二个方法,这个时候点击第二个按钮,第一个方法会被暂停。去执行第二个方法。当第二个方法执行完的时候,第一个方法才会被执行。
当然也看出来了。去掉lock。程序表现是一样的。
所以~~~题主的问题跟DoEvent()函数看似是没关系的。
接下来本人就开始想为什么被lock之后,语句还是可以执行呢。(就是连续点击两次。textbox的异步修改功能是重复出现的)想了一下。觉得还是Doenvent的问题。于是本人又修改了代码。

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private LockTest lt = new LockTest();
        private void button1_Click_1(object sender, EventArgs e)
        {
            lt.testc(textBox1,label1);
        }
        private void button2_Click(object sender, EventArgs e)
        {
            lt.testb(textBox1,label2);
        }
    }
    public class LockTest
    {
        private static object locker = new object();
        private int count = 1;
        public void testc(TextBox tb,Label lable)
        {
            lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("aaaa\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(100);
                    lable.Text = count.ToString();
                    count++;
                    Application.DoEvents();
                }
                tb.Invoke(new Action(() => tb.AppendText("axaxax\r\n")));
            }
        }
        public void testb(TextBox tb,Label lable)
        {
            lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("bbbb\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(100);
                    lable.Text = count.ToString();
                    count++;
                    Application.DoEvents();
                }
                tb.Invoke(new Action(() => tb.AppendText("bxbxbx\r\n")));
            }
        }
    }

这下就很清楚了。还是Doenvet的问题!这个函数,通俗来说。就是当他运行的时候,你可以用程序处理其他消息队列中的事情。但是只要处理,那么现在的消息就往后推~~~~其实就是把现在的线程和正在处理的消息分开,处理你让他的处理的另外一个消息。lock还是起了作用。但是被这个函数直接把线程干掉了。

5

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private LockTest lt = new LockTest();
        private void button1_Click_1(object sender, EventArgs e)
        {
            Task t = new TaskFactory().StartNew(() => lt.testc(this.textBox1,this.label1));
            
        }
        private void button2_Click(object sender, EventArgs e)
        {
            Task t = new TaskFactory().StartNew(() => lt.testb(this.textBox1, this.label2));
        }
    }
    public class LockTest
    {
        private static object locker = new object();
        private int count = 1;
        public void testc(TextBox tb,Label lable)
        {
            lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("aaaa\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(100);
                    lable.Invoke(new Action(() => lable.Text = count.ToString()));
                    count++;
                }
                tb.Invoke(new Action(() => tb.AppendText("axaxax\r\n")));
            }
        }
        public void testb(TextBox tb,Label lable)
        {
            lock (locker)
            {
                tb.Invoke(new Action(() => tb.AppendText("bbbb\r\n")));
                var sw = new Stopwatch();
                sw.Start();
                while (sw.ElapsedMilliseconds < 2000)
                {
                    Thread.Sleep(100);
                    lable.Invoke(new Action(() => lable.Text = count.ToString()));
                    count++;
                }
                tb.Invoke(new Action(() => tb.AppendText("bxbxbx\r\n")));
            }
        }
    }

刚修改了一下。这样就能看出。lock还是有用的。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明为什么 lock 没有起作用
喜欢 (0)
[1034331897@qq.com]
分享 (0)