线程同步,线程安全例子
namespace ConsoleApplication22
{
class Program
{
static void Main(string[] args)
{
Thread[] ts = new Thread[30];
TestDemo td = new TestDemo();
int i;
for (i = 0; i < 30; i++)
{
ts[i] = new Thread(td.Test1);
ts[i].Start();
}
while (true)
{
bool flag = td.Test2();
if (flag)
break;
}
Console.ReadKey();
}
}
public class TestDemo
{
private Hashtable sh = new Hashtable();
private static object SynLock = new object();
int i = 0;
public void Test1()
{
lock (SynLock)
{
++i;
Thread.Sleep(1000);
System.Diagnostics.Debug.WriteLine(i+"roy");
sh.Add(i, i);
}
}
public bool Test2()
{
if (i != 30 && !sh.ContainsKey(30))
return false;
lock (SynLock)
{
foreach (DictionaryEntry de in sh)
{
Console.WriteLine("..............."+de.Key.ToString()+"................"+de.Value.ToString());
}
return true;
}
}
}
}
internal class Account
{
int balance;
Random r = new Random();
// private static object synLock = new object();
internal Account(int initial)
{
balance = initial;
}
internal int Withdraw(int amount)
{
if (balance < 0)
{
//如果balance小于0则抛出异常
throw new Exception("Negative Balance");
}
//下面的代码保证在当前线程修改balance的值完成之前
//不会有其他线程也执行这段代码来修改balance的值
//因此,balance的值是不可能小于0 的
System.Diagnostics.Debug.WriteLine("Current Thread:" + Thread.CurrentThread.Name);
// Console.WriteLine("Current Thread:" + Thread.CurrentThread.Name);
//如果没有lock关键字的保护,那么可能在执行完if的条件判断之后
//另外一个线程却执行了balance=balance-amount修改了balance的值
//而这个修改对这个线程是不可见的,所以可能导致这时if的条件已经不成立了
//但是,这个线程却继续执行balance=balance-amount,所以导致balance可能小于0
if (balance >= amount)
{
// Thread.Sleep(5);
balance = balance - amount;
return amount;
}
else
{
return 0; // transaction rejected
}
}
TraceSource ts = new TraceSource("GetOMLogSource");
internal void DoTransactions()
{
lock (this)
{
// System.Diagnostics.Debug.WriteLine("Current Thread:" + Thread.CurrentThread.Name);
for (int i = 0; i < 100; i++)
Withdraw(r.Next(-50, 100));
}
}
}
class Program
{
static internal Thread[] threads = new Thread[10];
public static void Main()
{
Account acc = new Account(0);
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].Name = i.ToString();
for (int i = 0; i < 10; i++)
threads[i].Start();
Console.ReadLine();
}
}
线程安全: 顾名思义 就是 利用线程的同步技术,让多个线程 安全地 访问同一个资源(比如一个静态全局变量,静态全局集合等)
以下这些我经过单步调试,Trace日志跟踪,VS压力测试工具做并发测试得出来的正确结论!一直有个关于同时保证线程安全和并发性能的博弈。做了下相关研究得出了几种处理方式:线程安全,让多个线程安全地访问同一共享资源。通常都会用各种锁来保证线程同步使共享资源不会发生意外的逻辑错误或异常。但是加了锁以后,你系统中要读取共享资源的线程只能等待,因为锁使你的系统完全没有并发性,这个在多客户端对一个服务器的正式系统正用户体验出奇的差。 我这里就2个项目场景说说我的解决方式:(1)对于实时性不是非常高的系统,我们可以克隆一个共享资源对象的副本,对副本做了所有相关业务操作以后,才真正锁定共享资源,然后把副本对象的地址赋给共享资源,这样就做到了尽可能的极少时间锁定共享资源,提高了程序的并发性也保证了线程的安全。比如共享资源是服务端程序的一个dataset,N个客户端程序通过远程调用可以写这个dataset,也要求读这个dataset,当一个客户端要求修改这个dataset的时候,我通常会产生一个这个dataset的一个副本进行操作,操作完成以后锁定共享资源dataset把引用地址赋给它,这样其他要求读这个共享资源的客户端不会有阻塞。 (2)对于实时性要求非常高的系统比如IM系统,我会采取读写锁来处理这个问题,读写锁可以保证获取多个读锁,但只能获取一个更新锁或写锁(排他的)。举个例子再一个原子操作里将读锁升级为写锁是很有用的,例如,假设你想要再一个list 里面写一些不存在的项的时候, 你可能会执行下面的一些步骤:1获取一个读锁。2测试,如果要写的东西在列表中,那么释放锁,然后返回。3释放读锁。4获取一个写锁5添加项,写东西,6释放写锁。问题是:在第三步和第四步之间,可能有另一个线程修改了列表。一个可更新锁除了它可以在一个原子操作中变成写锁外很像一个读锁,你可以这样使用它:1调用EnterUpgradeableReadLock 获取可更新锁。2执行一些读操作,例如判断要写的东西在不在List中。3调用EnterWriteLock , 这个方法会将可更新锁 升级为 写锁。4执行写操作,5调用ExitWriteLock 方法,这个方法将写锁转换回可更新锁。6继续执行一些读操作,或什么都不做。6调用ExitUpgradeableReadLock 释放可更新锁。7从调用者的角度来看,它很像一个嵌套/递归锁,从功能上讲,在第三步,ReaderWriterLockSlim 在一个原子操作里面释放读锁,然后获取写锁。
根据错误提示,可知在调试器中运行应用程序时,如果不是控件的创建者线程试图调用该控件时,则调试器会引发 InvalidOperationException,并提示消息:“从不是创建控件 Control Name 的线程访问它。” 说白了,就是资源访问的不一致性,控件的调用者线程不是控件的创建者线程,这是.net实现资源安全访问机制的必然结果。访问 Windows 窗体控件本质上不是线程安全的。如果多个线程同时操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。
作者:ChenLuLouis
出处:http://www.cnblogs.com/chenlulouis/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-chenlulouisBlog。
posted on 2011-02-27 14:02 chenlulouis 阅读(1948) 评论(0) 编辑 收藏 举报