为什么 lock 没有起作用??

dangdang2471 2016-08-10 03:39:50
// 点击按钮后,执行输出如下:
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")));
}
}
}
...全文
586 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
摇撼大地 2016-08-10
  • 打赏
  • 举报
回复


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还是有用的。
摇撼大地 2016-08-10
  • 打赏
  • 举报
回复
看了楼主的问题。让我更深的了解了lock和DoEvent
摇撼大地 2016-08-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还是起了作用。但是被这个函数直接把线程干掉了。。。。。。
让我睡一会儿 2016-08-10
  • 打赏
  • 举报
回复
引用 4 楼 dangdang2471 的回复:
[quote=引用 1 楼 Forty2 的回复:] lock对不同线程来的请求会互斥。但是,同一个线程下"嵌套"的lock,并不互锁。
"同一个线程下"嵌套"的lock"怎么理解? button1_Click方法中的两个lt.testc(textBox1) 应该是在同一个线程中的,如果同线程的不互锁,为什么第二个方法要等第一个方法执行完才能执行呢?[/quote] 因为你这里写的while (sw.ElapsedMilliseconds < 2000) 两秒呀。只要连续的两次操作没有超过2秒就不会执行tb.Invoke(new Action(() => tb.AppendText("aaaaaaaaaaaa\r\n")));
dangdang2471 2016-08-10
  • 打赏
  • 举报
回复
引用 1 楼 Forty2 的回复:
lock对不同线程来的请求会互斥。但是,同一个线程下"嵌套"的lock,并不互锁。
"同一个线程下"嵌套"的lock"怎么理解? button1_Click方法中的两个lt.testc(textBox1) 应该是在同一个线程中的,如果同线程的不互锁,为什么第二个方法要等第一个方法执行完才能执行呢?
我是飞云 2016-08-10
  • 打赏
  • 举报
回复
楼上正解! 这里Invoke连异步都不是,所以线程是同一个的时候不会发生LOCK
Poopaye 2016-08-10
  • 打赏
  • 举报
回复
因为你压根就不知道lock是干啥用的 https://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.140).aspx
Forty2 2016-08-10
  • 打赏
  • 举报
回复
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是可以直接获得的。

110,565

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧