C#lock线程同步

 
以下内容均来自(C#高级编程学习捷径 刘洪成 编著 清华大学出版社)
1. 创建一个线程
using System;
using System.Threading;
 
namespace ConsoleApplication1
{
    class App
    {
        public void run()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i.ToString());
            }
        }
 
        static void Main(string[] args)
        {
            Console.WriteLine("App Start");
 
            App app = new App();
            ThreadStart ts = new ThreadStart(app.run);
            Thread t = new Thread(ts);
 
            t.Start();
            Console.WriteLine("App end");
        }
    }
}
 
2. 为线程设置名字
Thread有Name属性,可以设置它的名字。
 
3. Sleep函数
Sleep函数被调用时,该线程所占用的系统资源,一般来说是CPU,会被释放掉,从而其他线程可以得到一些资源以进行响应的操作。
 
4. 设置线程优先级
可以通过设置Priority属性来控制线程的优先级。但是我们对线程的运行次序永远无法做到真正控制,因为这个控制权主要在于操作系统。我们只能在整体上使一个线程获得更多或更少的运行机会而已。而并不是说在任何一个特定的时刻,一个高优先级的线程就一定会先于一个低优先级的线程被运行。
 
5. 线程的后台与前台运行
所谓运行在后台是说该线程与产生它的父进程紧密联系在一起,它隐藏在父进程的后面,所以一旦父进程终止运行,该线程本身也会被立即终止,不管线程是否已经真正结束。相反,一个运行在前端的线程却是独立于产生它的父进程,一旦该进程开始运行,即使父进程已经终止运行,这个线程却会照样独立运行,直到运行结束或者被其他程序强制终止。C#中的一个线程的默认设置为前台运行。
 
6. 终止一个进程
       6.1我们可以使用Abort方法来强行终止一个线程
       6.2 一个更好的方法是通过设置一个标志来告诉线程自动终止,程序如下
using System;
using System.Threading;
 
namespace ConsoleApplication1
{
    class App
    {
        private bool bContinue = true;
 
        public void run()
        {
            Thread t = Thread.CurrentThread;
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(t.Name + ": " + i.ToString());
                Thread.Sleep(1);
                if (!bContinue)
                {
                    return;
                }
            }
        }
 
        static void Main(string[] args)
        {
            Console.WriteLine("App Start");
 
            App app = new App();
            ThreadStart ts1 = new ThreadStart(app.run);
            Thread t1 = new Thread(ts1);
 
            t1.Name = "T1";
            t1.Start();
 
            Thread.Sleep(5);
 
            app.bContinue = false;
 
            Console.WriteLine("App end");
        }
    }
}
 
7. 线程的暂停与再运行
使用Suspend方法,我们可以暂停一个线程,如果要重新激活它,则使用Resume方法。
 
8. 线程Join函数
在激活一个线程后,我们可以等待该线程运行结束,或者设置一个最长的等待时间。只有等线程自动结束或者已经运行超过了前面预先设定的时间后,才去接着运行后面的程序。
using System;
using System.Threading;
 
namespace ConsoleApplication1
{
    class App
    {
        public void run()
        {
            Thread t = Thread.CurrentThread;
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(t.Name + ": " + i.ToString());
                Thread.Sleep(10);
            }
        }
 
        static void Main(string[] args)
        {
            Console.WriteLine("App Start");
 
            App app = new App();
            ThreadStart ts1 = new ThreadStart(app.run);
            ThreadStart ts2 = new ThreadStart(app.run);
            Thread t1 = new Thread(ts1);
            Thread t2 = new Thread(ts2);
 
            t1.Name = "T1";
            t2.Name = "T2";
            t1.Start();
            t1.Join(40);
            t2.Start();
            t2.Join();
 
            Console.WriteLine("App end");
        }
    }
}
在这个程序中,T1运行40微秒后,T2才有机会运行,而主程序则是一直等待T2结束后,才去运行最后一个打印语句。
 
9. 线程加锁
线程加锁可以放置同一时刻,多个线程对资源的争夺。使它们能够正确的访问资源。
using System;
using System.Threading;
 
namespace ConsoleApplication1
{
    class App
    {
        public int i = 0;
        public int j = 0;
        public int Limit = 100;
        public void Increase()
        {
            while (i < Limit)
            {
                i++;
                Thread.Sleep(1);
                j++;
            }
        }
 
        public void Check()
        {
            while (i < Limit)
            {
                if (i != j)
                {
                    Console.WriteLine("i != j, i = {0}, j = {1}", i, j);
                    Thread t = Thread.CurrentThread;
                    t.Abort();
                }
                Thread.Sleep(1);
            }
        }
 
        static void Main(string[] args)
        {
            App app = new App();
 
            Thread t1 = new Thread(new ThreadStart(app.Increase));
            Thread t2 = new Thread(new ThreadStart(app.Increase));
            Thread t3 = new Thread(new ThreadStart(app.Check));
 
            t1.Start();
            t2.Start();
            t3.Start();
        }
    }
}
在这个例子中,我们看到,每当i被加1时,j也总是被同时加1,所以看来i与j应该永远相等,可事实上,我们得到如下结果:
i != j, i = 2, j = 0
原因在于,i与j的累加和i与j的检测发生在两个不同的线程中。可能发生的情况是,在一愕嘎线程中i刚被累加过,但是j还没有;或者是前两个线程互相覆盖了累加的结果(假设在某一点上,线程A与线程B同时读取i的值为5,此时,j的值也是5,但两个线程都还没有去读取它,然后线程A将i加上1,即i的值变为6,可是同样的,线程B又在5的基础上将i加1,再次将i设置为6,所以i最终被设置为6。但是两个线程对j的读取却是错开进行的,也就是说当线程A将j的值改变为6后,线程B才去读取,从而发现j的值为6,并在此基础上将其加1,最终将j的值设定为7)。这样在测试程序中,便发现i与j的值不等了。这便是线程之间的资源共享问题。
C#为解决资源共享问题引进了Monitor的概念,也就是说,在某个线程要运行一段程序前先得获取一个监控器,从而通知其他线程不可能同时再运行这同一段程序。比如,上面的例子中的Increase函数可以修改如下:
public void Increase()
        {
            while (i < Limit)
            {
                try
                {
                    Monitor.Enter(this);
                    i++;
                    Thread.Sleep(1);
                    j++;
                }
                finally
                {
                    Monitor.Exit(this);
                }
            }
        }
这样便解决了两个累加线程之间相互覆盖的问题。可是这样i与j还是可能不等,我们必须给检测函数也加一个监控器。这样无论何时,ij总是相等的了。
再实际应用中,我们总是将Monitor.Enter与Monitor.Exit同时运用,C#又提供了lock语句来实现对一段程序的加锁,因而程序可以简化为
public void Increase()
        {
            while (i < Limit)
            {
                lock (this)
                {
                    i++;
                    Thread.Sleep(1);
                    j++;
                }
            }
        }
同样要lock住Check函数。
注意:虽然我们对这两个函数加了锁,但是我们还是不能保证任何适合ij的值在任何程序中都是相等的。比如我们添加一个函数,然后添加一个线程。ij就可能不等了。所以,最彻底的解决办法是,对i和j的访问加锁。最终的程序如下:
using System;
using System.Threading;
 
namespace ConsoleApplication1
{
    class App
    {
        public int i = 0;
        public int j = 0;
        public int Limit = 100;
        public void Increase()
        {
            while (i < Limit)
            {
                lock (this)
                {
                    i++;
                    Thread.Sleep(1);
                    j++;
                }
            }
        }
 
        public int I
        {
            get
            {
                lock (this)
                {
                    return i;
                }
            }
            set
            {
                lock (this)
                {
                    i = value;
                }
            }
        }
 
        public int J
        {
            get
            {
                lock (this)
                {
                    return j;
                }
            }
            set
            {
                lock (this)
                {
                    j = value;
                }
            }
        }
 
        public void Check()
        {
            while (I < Limit)
            {
                if (I != J)
                {
                    Console.WriteLine("I != J, I = {0}, J = {1}", I, J);
                    Thread t = Thread.CurrentThread;
                    t.Abort();
                }
                Thread.Sleep(1);
            }
        }
 
        static void Main(string[] args)
        {
            App app = new App();
 
            Thread t1 = new Thread(new ThreadStart(app.Increase));
            Thread t2 = new Thread(new ThreadStart(app.Increase));
            Thread t3 = new Thread(new ThreadStart(app.Check));
 
            t1.Start();
            t2.Start();
            t3.Start();
        }
    }
}
评论 2 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值