好久没写博客了,可能是因为最近工作太过于压抑的原因吧!有点颓废了.... 而且公司距离住处要坐公交将近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. IIS7配置PHP简要说明

    1. IIS7 安装的时候 要注意三个地方打得勾 万维网服务->应用程序开发功能->CGI ->ISAPI扩展 ->ISAPI筛选器 注:   CGI  会在IIS7+PHP_ ...

  2. 条款52:谢了placement new 也就同时应该写一个placement delete

    如果operator new接收到的参数除了size_t之外还有其他的话,那么这个operator new实际上就是一个placement new,所以考虑下下面这样的情况: 一个可以用来记录信息的p ...

  3. 又是毕业季1&&又是毕业季2

    又是毕业季2 n/k; 又是毕业季2 一开始很容易想到枚举n个数取k个的所有组合,然后分别用辗转相除法求最大公约数,但是复杂度明显不符合要求,于是必须换一种思路. 我们想到,k个数的公约数含义就是这k ...

  4. 20165210 Java第一周学习总结

    20165210 2018<Java程序设计>第一周总结 教材学习内容总结 第一章知识要点 Java在当代需求量极高 Java程序不依赖平台 Java内置对多线程的支持 重点安装JDK 源 ...

  5. bzoj 5403 Marshland

    $n \leq 50$ sol: 放一个在 $x$ 处拐弯的 $L$ 形石头相当于在水平和垂直方向上各选一个与 $x$ 相邻的点,全局不能重复选 最小化危险度,相当于满足这些限制的情况下石头盖住的点危 ...

  6. CodeForces - 961D:Pair Of Lines (几何,问两条直线是否可以覆盖所有点)

    You are given n points on Cartesian plane. Every point is a lattice point (i. e. both of its coordin ...

  7. Qt之事件处理机制

    思维导读 一.事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下 ...

  8. JSONP解决跨域方案

    一.jsonp原理 本质并不是ajax,只是执行了跨域js,所以该方式只支持get方式 html中,所有带src属性的标签都可以跨域script img iframe 所以,可以通过script加载其 ...

  9. Oracle查询数据表结构(字段,类型,大小,备注)

    作用:想要生成整个Oracle数据库所有表结构WORD文档(数据库设计说明书) Oracle数据库字典介绍    Oracle数据字典是有表和视图组成的,存储有关数据库结构信息的一些数据库对象.数据库 ...

  10. Java实现Queue类

    Java实现Queue类 import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Sc ...