c # thread4 - lock, deadlock, and monitor keywords

Multithreading existence is to improve the efficiency of the system, a means to tap the cpu performance, control it, multiple threads can collaborate bug does not occur is the key.

First we look at a multi-threaded code unsafe.

public  abstract  class CalculateBase
    {
        public int count = 0;
        public object _lock = new object();
        public abstract void Operation();
    }

    public  class NoSafeCalculate: CalculateBase
    {
        public override void Operation()
        {
                count++;
                count--;
        }
    }
static void Main(string[] args)
        {
            CalculateBase calculate = new NoSafeCalculate();
            Thread thread1 = new Thread(() =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    calculate.Operation();
                }
            });
            Thread thread2 = new Thread(() =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    calculate.Operation();
                }
            });
            thread1.Start();
            thread2.Start();
            thread1.Join();
            thread2.Join();
            Console.WriteLine(calculate.count);
            Console.ReadLine();

        }

Very simple piece of code, roughly meaning to start two threads plus or minus a variable, because it is an instance of an object, so the variables are the same, Logically, variable increment decrement once again, it should be the same , indeed in a single thread, but it will change in multiple threads results.

We performed it.

 

 

Findings is not 0, but 226. and each execution is not the same result.

Why does this happen?

I believe many people know

1 ++ - not atomic, for example a ++ is actually a multi-step operation. 1) the value of a + 1 is calculated. 2) The value assigned to a, so many threads in the implementation of this operation is very easy to cause concurrency problems, inconsistent results. (C # comes atomicity operation function)

2. inconsistent values. For example, my side of the count in the execution ++ operator, but has not yet been performed, another thread to be executed ++ operating result after two ++ executed, but ultimately + once.

To solve this problem multithreading, if you do not consider the performance, then the lock keyword is a good choice

Let's look at security code

public  abstract  class CalculateBase
    {
        public int count = 0;
        public object _lock = new object();
        public abstract void Operation();
    }

    public  class NoSafeCalculate: CalculateBase
    {
        public override void Operation()
        {
                count++;
                count--;
        }
    }
    public class SafeCalculate : CalculateBase
    {
        public override void Operation()
        {
            lock (_lock)
            {
                count++;
                count--;
            }
        }
    }
static void Main(string[] args)
        {
            CalculateBase calculate = new SafeCalculate();
            Thread thread1 = new Thread(() =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    calculate.Operation();
                }
            });
            Thread thread2 = new Thread(() =>
            {
                for (int i = 0; i < 1000000; i++)
                {
                    calculate.Operation();
                }
            });
            thread1.Start();
            thread2.Start();
            thread1.Join();
            thread2.Join();
            Console.WriteLine(calculate.count);
            Console.ReadLine();

        }

Obviously, compared to insecure code we have more than just a lock of scope, we added a lock in Operation method scope and increase our self decrement wrapped up, and pass an object of type _lock parameters.

Results: 0

 

 

 Several tests results are zero, it can be said that line the city is safe, then what is the role of lock keyword yes.

c # lock keyword in java equivalent of synchionzed, meaning "sync", that is to say the way it was wrapped in the code segment will be synchronized, that is, while single-threaded execution.

So how is it done? In fact, the role of the parameters passed, we see a _lock parameters passed, in fact, it is a "key lock", who can get whoever he can play lock, executing code. So every time there is a thread get the lock, other threads can only wait for this thread and then executing the lock release, other threads can continue to perform.

(Additional: Why object type as a key to the lock, string or basic types can it make any difference?)

What is a deadlock

Thread deadlock is due to two or more threads hold resources needed for each other will wait for each other to release resources, resulting in these threads in a wait state, unable to travel to perform. If the thread does not take the initiative to release any resources, will result in a deadlock.

We accomplished a deadlock demo

 

public class DeadLock
    {
        public object _lock = new object();
        public object _lock1 = new object();
        public void Into()
        {
            lock (_lock)
            {
                Thread.Sleep(2000);
                lock (_lock1)
                {
                    Console.WriteLine("I am success come in");
                }
            }
        }
        public void Out()
        {
            lock (_lock1)
            {
                Thread.Sleep(1000);
                lock (_lock)
                {
                    Console.WriteLine("I am success go out");
                }
            }
        }
    }

 

static void Main(string[] args)
        {
            DeadLock dead = new DeadLock();
            Thread thread1 = new Thread(() => { dead.Into(); });
            Thread thread2 = new Thread(() => { dead.Out(); });
            thread1.Start();
            thread2.Start();
            thread1.Join();
            thread2.Join();
            Console.WriteLine("执行结束");
        }

运行代码:

 

 那基本上是这辈子看不到打印"执行结束"这四个字了。

其实上面的例子就是一个死锁的典型场景,一个线程拿住了A钥匙,打开了A锁,执行完后需要B钥匙,才能打开B锁,可是B钥匙永远也拿不到,因为另外一个线程正占用这B钥匙,在等待A锁的释放

那么怎么解决这种问题呢。

1.尽量用不同的对象作为锁的"钥匙"

2.使用c#提供的monitor关键字

我们将上面的代码进行修改。

 

public class DeadLock
    {
        public object _lock = new object();
        public object _lock1 = new object();
        public void Into()
        {
            lock (_lock)
            {
                Thread.Sleep(2000);
                lock (_lock1)
                {
                    Console.WriteLine("I am success come in");
                }
            }
        }
        public void Out()
        {
            lock (_lock1)
            {
                Thread.Sleep(1000);
                if (Monitor.TryEnter(_lock, 5))
                {
                    Console.WriteLine("go out success");
                }
                else
                {
                    Console.WriteLine("timeout");
                }

            }
        }
    }

 

此时我们再来看看运行结果:

 

 可以看到我们的程序死锁走出来了。

我们的monitor的作用就是会接受一个超时的参数,如果在时间内拿到锁,则返回true否则返回false,避免了死锁

最后补充下,其实lock也是Monitor的一个语法糖,分解lock的代码我们就能够了解。

 

public void demoLock()
        {
            bool getlock = false;
            try
            {
                Monitor.Enter(_lock, ref getlock);
            }
            finally
            {
                if (getlock)//如果拿到锁则释放锁
                {
                    //业务
                    //...
                    Monitor.Exit(_lock);
                }
            }
        }

 

 

 

 

Guess you like

Origin www.cnblogs.com/qwqwQAQ/p/11980498.html