好久没写博客了,可能是因为最近工作太过于压抑的原因吧!有点颓废了.... 而且公司距离住处要坐公交将近40--50分钟(各个原因,纠结中ing...),提前一个半小时起床,居然还能迟到!因为距离公司前两站是个十字路口,每天能在哪里塞上30多分钟....眼看就要到公司了,一辆接一辆阻塞着..看着时间一分一秒的过去..心里不尽拔凉起来(迟到会扣钱的!)... 拔凉之余不禁让我想到C#中线程的同步异步.所以呈此博文,来谈谈我对C#中线程同步的理解,不当之处,请大家多多指点,在此先谢谢了!

什么是线程同步? 多个线程同时运行时,可能会因为线程之间的逻辑关系而决定谁先执行,谁后执行, 这就是线程同步。

(1)线程的优先级

当线程之间争夺CPU时间片时,CPU是按照线程的优先级进行服务的。在C#应用程序中,线程有5个不同的优先级,由高到低分别是:Highest、AboveNormal、Normal 、BelowNormal和Lowest。创建线程时,如果不指定其优先级,则系统默认为Normal。

如:Thread t = new Thread(MethodName);

    t.priority = ThreadPriority.AboveNormal;

通过设置线程的优先级可以改变线程执行的顺序,所设置的优先级仅仅适用于这些线程所属的进程。(注:当把某个线程的优先级设置为Htghest时,系统上正在运行的其他线程都会终止,所以使用这个优先级的时候要特别小心。除非遇到“币需”马上处理的任务,否则不要使用这个优先级)。

(2)线程同步

多线程处理解决了吞吐量和响应速度的问题,但同时也带来了资源共享问题,如死锁和资源争用。多线程特别适用于需要不同的资源(如文件句柄和网络连接)的任务。为单个资源分配多个线程可能会导致同步问题,这种情况下,线程可能会北频繁阻止以等待其他线程,从而使用多线程的初衷背道而驰。

所谓同步,是指多个线程之间存在先后执行顺序的关联关系。如果一个线程必须在另一个线程完成某个工作后才能继续执行,则必须考虑如何让其他保持同步,以确保在系统上同时运行多个线程而不会出现死锁或逻辑错误。

为了解决同步问题,一般使用辅助线程执行不需要大量占用其他线程所使用的资源的耗时任务或时间要求紧迫的任务。但实际上,程序中的某些资源必须由多个线程访问。为了解决这些问题,System.Threading命名空间提供了多个用于同步线程的类。这些类包括Mutex,Monitor,Interlocked,AutoResetEvent.  但是实际应用程序中,我们使用最多的可能不是这些类,而是C#提供的lock语句。

 Lock语句

    为了在多线程应用程序中让同步变得简单,C#专门提供了一个lock语句。lock关键字能确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码段,则它将一直等待(阻塞),知道锁定的对象被释放以后才能进入临界区。

private Object obj = new Object();
.....
lock(obj)
{
//临界区
}

举个例子相信大家会更明白,路人甲和路人乙要上厕所,刚好找到了一个公共厕所,杯具的是公共厕所里面只有一个位置,路人甲是会员(优先级高),先溜进去了,然后把门锁上(Lock)紧接着里面发出一阵阵巨响....(大家都懂的,最近食物不敢乱吃啊 - -!)。路人乙可着急了,捂着肚子,在外面打转,憋得面红耳赤!过了好一段时间,路人甲抽着香烟,吹着口哨,从厕所里面走出来(Lock解锁了),路人乙急忙钻进去,紧接着又是一阵巨响.....

虽然这个例子举的有点不和谐,但相信大家已经弄明白Lock的作用了。

值得注意的是:1、锁定的对象名(上面的obj),一般声明为Object类型,注意不要将其声明为值类型,对象名叫什么无所谓,只要符合对象命名原则就行。2、一定要将该Object类型的对象名声明为private(私有),不能将其声明为public(公共),否则将会使lock语句无法控制,从而引发一系列的问题。 (就像上面举例一样:漆黑不见五指的夜晚(没电),路人乙解开裤带,正准备蹲下时,一只手把路人乙的PP托住,喊道:有人!- -#。)3、处于临界区的代码不宜太多。如果在锁定和解锁期间处理的代码过多,由于某个线程执行临界区中的代码时,其他等待运行临界区中代码的线程都会处于阻塞状态,这样就可能会降低应用程序的性能。(路人乙会恨死路人甲的!)

好了,闲话就说这么多,还是拿代码说事吧,说过随机取款的例子。

  (1)新建一个名为LockExample的Windows应用程序,放下一个listbox,一个button,界面如下:

  (2)添加一个类:Account.cs。代码如下:

class Account
    {
        private Object obj = new object();
        int balance;
        Random rd = new Random();
        Form1 form1;
 
        public Account(int initial,Form1 form1)
        {
            this.form1 = form1;
            this.balance = initial;
        }
 
        /// <summary>
        /// Withdraws the specified amount.
        /// </summary>
        /// <param name="amount">The amount.</param>
        /// <returns></returns>
        private int Withdraw(int amount)
        {
            if (balance < 0)
            {
                form1.AddListBoxItem("余额" + balance + " 兄弟,你以为你这是信用卡啊!还钱吧!");
            }
 
            //将lock(lockedobj)这句话注视掉,看看会发生什么情况
            lock (obj)
            {
                if (balance >= amount)
                {
                    string str = Thread.CurrentThread.Name + "取款---";
                    str += string.Format("取款前余额:{0,-6}取款:{1,-6}",balance,amount);
                    balance = balance - amount;
                    str += "取款前余额:" + balance;
                    form1.AddListBoxItem(str);
                    return amount;
                }
                else
                {
                    return 0;
                }
            }
        }
 
        public void DoTransactions()
        {
            for (int i = 0; i < 100; i++)
            {
                Withdraw(rd.Next(1,100));
            }
        }
    }

  (3)切换到Form1.cs代码编辑界面,写入一下代码:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} private void btnLock_Click(object sender, EventArgs e)
{
lbLock.Items.Clear();
Thread[] threads = new Thread[10];
Account acc = new Account(1000,this);
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(acc.DoTransactions);
t.Name = "线程" + i;
threads[i] = t;
} for (int i = 0; i < 10; i++)
{
threads[i].Start();
}
} delegate void AddListBoxItemDelegate(string str);
public void AddListBoxItem(string str)
{
if (lbLock.InvokeRequired)
{
AddListBoxItemDelegate d = AddListBoxItem;
lbLock.Invoke(d, str);
}
else
{
lbLock.Items.Add(str);
}
}
}

  (4)按<F5>编译并运行,单击 “开始自动随机取款”按钮,观察线程执行后在listbox中添加的可能出现的内容,如图:

(5) 将lock(obj)这条语句注视掉,再次运行程序,观察线程执行后在listbox中添加的可能出现的内容,如图:

如图中线程6取款后余额已经是584了,但是在线程7取款时候 余额又变成了746.显然结果不正确。

好了,对线程同步问题和解决同步问题的理解,就先写道这里,上述不当之处,希望大家多多指点,共同进步。


作者:康忠鑫(Stephe
n.Kang) 
出处:http://www.cnblogs.com/axing/ 
Q群:诚邀C#和.NET方面志同道合的朋友加入:CSharpAndDotNET 研发群——>13983671 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

从公交塞车,看C#多线程问题(转)的更多相关文章

  1. 从ConcurrentHashMap的演进看Java多线程核心技术 Java进阶(六)

    本文分析了HashMap的实现原理,以及resize可能引起死循环和Fast-fail等线程不安全行为.同时结合源码从数据结构,寻址方式,同步方式,计算size等角度分析了JDK 1.7和JDK 1. ...

  2. 031.[转] 从类状态看Java多线程安全并发

    从类状态看Java多线程安全并发 pphh发布于2018年9月16日 对于Java开发人员来说,i++的并发不安全是人所共知,但是它真的有那么不安全么? 在开发Java代码时,如何能够避免多线程并发出 ...

  3. 再看python多线程------threading模块

    现在把关于多线程的能想到的需要注意的点记录一下: 关于threading模块: 1.关于 传参问题 如果调用的子线程函数需要传参,要在参数后面加一个“,”否则会抛参数异常的错误. 如下: for i ...

  4. 多线程 or 多进程?[转]

    在Unix上编程采用多线程还是多进程的争执由来已久,这种争执最常见到在C/S通讯中服务端并发技术的选型上,比如WEB服务器技术中,Apache是 采用多进程的(perfork模式,每客户连接对应一个进 ...

  5. 多线程 or 多进程 (转强力推荐)

    在Unix上编程采用多线程还是多进程的争执由来已久,这种争执最常见到在C/S通讯中服务端并发技术 的选型上,比如WEB服务器技术中,Apache是采用多进程的(perfork模式,每客户连接对应一个进 ...

  6. apache http server 多线程模式

    一般apache采用prefork和worker机制,通过apachectl -l命令查看默认使用的prefork机制.需要修改prefork策略 那么需要做如下修改: 1,/usr/local/ap ...

  7. Java 程序员们值得一看的好书推荐

    "学习的最好途径就是看书",这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一 ...

  8. [.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中)

    [.net 面向对象程序设计进阶] (17) 多线程(Multithreading)(二) 利用多线程提高程序性能(中) 本节要点: 上节介绍了多线程的基本使用方法和基本应用示例,本节深入介绍.NET ...

  9. iOS多线程到底不安全在哪里?

    iOS多线程安全的概念在很多地方都会遇到,为什么不安全,不安全又该怎么去定义,其实是个值得深究的话题. 共享状态,多线程共同访问某个对象的property,在iOS编程里是很普遍的使用场景,我们就从P ...

随机推荐

  1. 如何直接在网页中显示PDF文件

    通过的浏览器:360.Firefox.IE.Chrome 2. 下面这个完整点: <param name="_Version" value="65539" ...

  2. 解决Navicat连接mysql报错:1862 - Your password has expired. To log in you must change it using a client that supports expired passwords.

    今天尝试用Navicat连接mysql时,发现一个1862的报错问题: 后来参照这篇文章https://blog.csdn.net/u010513756/article/details/5073501 ...

  3. JAVA实现多线程的四种方式

    JAVA多线程实现方式: 1.继承Thread类(无返回值) 2.实现Runnable接口(无返回值) 3.实现Callable接口,通过FutureTask包装器来创建Threak线程(有返回值) ...

  4. HANA 存储过程

    You can develop secure procedures using SQLScript in SAP HANA by observing the following recommendat ...

  5. 原生js模态框实现

    <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...

  6. python 换行

    python3 end = “”:输入参数不换行. 就是打印之后不换行. python字符串换行: (1)三个单引号,例如print '''我是程序员 刚学习python''' (2)三个双引号,例如 ...

  7. webservice 传输数据过大,解析失败

    ERROR 错误信息: 已超过传入消息(65536)的最大消息大小配额.若要增加配额,请使用相应绑定元素上的 MaxReceivedMessageSize 属性. 错误场景: webservice 服 ...

  8. mysql 数据备份及pymysql模块

    一 IDE工具介绍 生产环境还是推荐使用mysql命令行,但为了方便我们测试,可以使用IDE工具 下载链接:https://pan.baidu.com/s/1bpo5mqj 掌握: #1. 测试+链接 ...

  9. Python&&ipython安装注意事项

    yum源里没有,需要先安装一个epel-release这个包,它提供的yum源里有,然后在yum install python-pip.ftp://ftp.muug.mb.ca/mirror/cent ...

  10. 2017.11.28 Enginering management:problem-solving ability

    Today,my colleague is on bussiness trip. going to customer factory in jiangxi. slove the color diffe ...