多线程技术(五)线程的同步

线程的同步
使用线程的一个重要方面是同步访问多个线程访问的任何变量。
背景:当多个线程共享数据,其中一个或多个线程要修改数据时,有可能引起数据不统一等问题。
同步:是指在某一时刻只有一个线程可以访问某共享数据

1、同步的含义
同步问题的产生,主要是由于在高级语言的源代码中,大多数情况下看起来是一条语句,但在最后编译好的汇编语言机器码中则会被翻译为许多条语句,从而在操作系统

调度时被划分到不同的时间片中。
例如:message += "Hello world!"; 
这条语句在C#语法上是一条语句,但在执行代码时,实际上它涉及到许多操作。需要重新分配内存以存储更长的新字符串,需要设置变量message使之指向新的内存,需要复制实际文本等。

2、在C#中处理同步
通过对指定对象的加锁和解锁可以实现同步代码段的访问。
在.NET的System.Threading命名空间中提供了Monitor类来实现加锁与解锁。该类中的方法都是静态的。如下表:

C#中 lock关键字提供了与Monitoy.Enter和Monitoy.Exit同样的功能,这种方法用在你的代码段不能被其他独立的线程中断的情况。通过对Monitor类的简易封装,lock为同步访问变量提供了一个非常简单的方式,其用法如下:
lock(x)
{
 // 使用x的语句
}
lock语句把变量放在圆括号中,以包装对象,称为独占锁或排它锁。当执行带有lock关键字的复合语句时,独占锁会保留下来。当变量被包装在独占锁中时,其他线程就不能访问该变量。如果在上面的代码中使用独占锁,在执行复合语句时,这个线程就会失去其时间片。如果下一个获得时间片的线程试图访问变量,就会被拒绝。

Windows会让其他线程处于睡眠状态,直到解除了独占锁为止。

【例9.2】使用lock同步线程。本示例建立了10个线程

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace TestThreadLock
{
    class Account
    {
        int balance;
        Random r = new Random();
        public Account(int initial)
        {
            balance = initial;
        }
        //取钱
        int Withdraw(int amount)
        {
            if (balance < 0)
                throw new Exception("余额为负!");
            lock (this)
            {
                if (balance >= amount)
                {
                    Console.WriteLine("原有余额:" + balance);
                    Console.WriteLine("支取金额:" + amount);
                    balance = balance - amount;
                    Console.WriteLine("现有余额:" + balance);
                    return amount;
                }
                else
                { 
                    return 0; //拒绝交易
                }
            }
        }
        public void DoTransactions()
        {
            //支取随机的金额100次
            for (int i = 0; i < 100; i++)
                Withdraw(r.Next(1, 100));
        }
    }
    class TestApp
    {
        static void Main(string[] args)
        {
            Thread[] threads = new Thread[10];
            Account acc = new Account(1000);
            for (int i = 0; i < 10; i++)
            {
                Thread t = new Thread(new ThreadStart(acc.DoTransactions));
                threads[i] = t;
            }
            for (int i = 0; i < 10; i++)
            {
                threads[i].Start();
            }
            Console.ReadKey();
        }
    }
}

同步时要注意的问题

 线程同步非常重要,但只在需要时使用也是非常重要的。因为这会降低性能。原因有两个:

首先,在对象上放置和解开锁会带来某些系统开销,但这些系统开销都非常小。第二个原因更为重要,线程同步使用得越多,等待释放对象的线程就越多。如果一个线程在对象上放置了一个锁,需要访问该对象的其他线程就只能暂停执行,直到该锁被解开,才能继续执行。因此,在lock块内部编写的代码越少越好,以免出现线程同步错误。lock语句在某种意义上就是临时禁用应用程序的多线程功能,也就临时删除了多线程的各种优势。
 

posted on 2012-07-31 10:21  流星落  阅读(390)  评论(0编辑  收藏  举报

导航