asp.net 多线程编程详解(1/2)

在线程间共享数据有可能会导致竞速状态而发生数据不一致的状态, 例如:

 代码如下 复制代码
namespace TaskParallel
{
    class Account
    {
        public int Balance
        {
            get;
            set;
        }
    }
    class Share
    {
        static void Main(string[] args)
        {
            Account account = new Account { Balance = 0 };
            List<Task> tasks = new List<Task>();
            for (int i = 0; i < 10; i++)
            {
                tasks.Add(Task.Factory.StartNew(() =>
                 {
                     for (int j = 0; j < 1000; j++)
                         account.Balance++;    
                 }));
            }
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine(account.Balance);           
        }
    }
}

这段程序中,一共有10个线程,每个线程将一个整型变量自加1000次,期待的结果最终应该是10000,但是运行这段程序的结果每次都不一样而且总比10000小。原因是完成一个变量自加并不是一个原子操作,忽略具体的机器代码不谈,总体上应该是三步,读取当前值,加1,存回计算值.如果线程1读取到了当前值是0,此时被线程2取代而进入等待状态,线程二读取当前值为0,加1,把1存回,线程1接着运行,加1,把1存回。此时Balance的值是1,而已经有线程1和线程2加了2次,数据不一致发生了。下面介绍.Net提供的线程互斥的方法,其实现原理在操作系统原理类的书上有详细介绍,不再赘述。

 

1. 使用Monitor

为了避免不一致发生,必须保证能够改变共享数据的代码在同一时间只有一个线程在执行,要实现这一点,可以使用C#的lock关键字:

 代码如下 复制代码
object obj = new object();for (int i = 0; i < 10; i++)
{
     tasks.Add(Task.Factory.StartNew(() =>
     {
           for (int j = 0; j < 1000; j++)
           {
                 lock (obj)
                 {
                      account.Balance++;
                 }
            }
        }));
}

lock其实是Monitor类的一个包装,要使用更为完整的功能可以使用Monitor类.2.使用Spin Locking

Spin Locking和Monitor实现的效果相似,但是原理不一样。Spin Locking不阻塞当前线程,而是用一个循环来不断判断是否符合访问条件。当预期阻塞的时间不太长的时候,他比Monitor类高效,但是不适合需要长时间阻塞的情况.

 代码如下 复制代码
SpinLock locker = new SpinLock();           
for (int i = 0; i < 10; i++)
{     tasks.Add(Task.Factory.StartNew(() =>
     {
          for (int j = 0; j < 1000; j++)
          {
               bool lockAcquired = false;
               try
               {
                   locker.Enter(ref lockAcquired);
                   account.Balance++;
               }
               finally
               {
                    locker.Exit();
               }                      
           }
       }));
}

3.使用Mutex,Semaphore

Mutex,Semaphore都继承自WaitHandle类,可以实现线程互斥的。WaitHandle是windows的synchronization handles的包装。

先介绍Mutex的例子:

 代码如下 复制代码
Mutex mutex = new Mutex();
for (int i = 0; i < 10; i++)
{
     tasks.Add(Task.Factory.StartNew(() =>
     {
         for (int j = 0; j < 1000; j++)
         {
              bool lockAcquired = mutex.WaitOne();
              account.Balance++;                     if(lockAcquired)
                 mutex.ReleaseMutex(); 
          }
     }));
}

WaitHandle的WaitAll方法可以同时获得多个锁,例如在下面的程序中,有两个账户,需要两个锁来保持他们在同一时间只有一个线程访问。其中第三个线程同时访问这两个账户,因此需要同时获得这个两个账户的锁,当第三个线程结束访问的时候,要记得释放两个锁。

 

 代码如下 复制代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TaskParallel
{
    class Account
    {
        public int Balance
        {
            get;
            set;
        }
    }
    class Share
    {
        static void Main(string[] args)
        {
            Account account1 = new Account { Balance = 0 };
            Account account2 = new Account { Balance = 0 };
            Mutex mutex1=new Mutex();
            Mutex mutex2=new Mutex();

            Task t1 = new Task(() =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    bool locked = mutex1.WaitOne();
                    account1.Balance++;
                    if (locked)
                        mutex1.ReleaseMutex();
                }
            });

            Task t2 = new Task(() =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    bool locked = mutex2.WaitOne();
                    account2.Balance++;
                    if (locked)
                        mutex2.ReleaseMutex();
                }
            });

            Task t3 = new Task(() =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    bool locked=WaitHandle.WaitAll(new WaitHandle[] { mutex1, mutex2 });
                    account1.Balance--;
                    account2.Balance--;
                    if (locked)   //release two locks
                    {
                        mutex1.ReleaseMutex();
                        mutex2.ReleaseMutex();
                    }
                }
            });

            t1.Start();
            t2.Start();
            t3.Start();
            Task.WaitAll(t1, t2, t3);
            Console.Write("Balance1:{0}, Balance2:{1}", account1.Balance, account2.Balance);
            Console.ReadLine();
        }
    }
}

首页 1 2 末页

时间: 2024-08-03 13:37:46

asp.net 多线程编程详解(1/2)的相关文章

linux多线程编程详解教程

 这篇文章主要介绍了linux多线程编程详解教程,提供线程通过信号量实现通信的代码,大家参考使用吧 线程分类   线程按照其调度者可以分为用户级线程和核心级线程两种.   (1)用户级线程  用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持.在这里,操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创建.调度.撤销等功能,而内核仍然仅对进程进行管理.如果一个进程中的某一个线程调用了一个阻塞的系统调用,那么该进程包括该进程

Java多线程编程详解

编程|多线程|详解 一:理解多线程多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立. 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单.多个线程的执行是并发的,也就是在逻辑上"同时",而不管是否是物理上的"同时".如果系统只有一个CPU,那么真正的"同时"是不可

VC多线程编程详解_C 语言

本文实例讲述了VC多线程编程概念与技巧,分享给大家供大家参考.具体分析如下: 一.多线程编程要点 线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源,包括打开的文件.信号标识及动态分配的内存等.一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度程序控制,调度程序决定哪个线程可执行以及什么时候执行线程.线程有优先级别,优先权较低的线程必须等到优先权较高的线程执行完后再执行.在多处理器的机器上,调度程序可将多个线程放到不同的处理器上去运行,这样可

linux多线程编程详解教程(线程通过信号量实现通信代码)_linux shell

线程分类 线程按照其调度者可以分为用户级线程和核心级线程两种. (1)用户级线程 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持.在这里,操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创建.调度.撤销等功能,而内核仍然仅对进程进行管理.如果一个进程中的某一个线程调用了一个阻塞的系统调用,那么该进程包括该进程中的其他所有线程也同时被阻塞.这种用户级线程的主要缺点是在一个进程中的多个线程的调度中无法发挥多处理器的优势.

IOS 多线程GCD详解_IOS

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取. #definedispatch_get_main_queue() \DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q) 可以看出,dispatch_get_main_queue也是一种disp

java 多线程-锁详解及示例代码_java

自 Java 5 开始,java.util.concurrent.locks 包中包含了一些锁的实现,因此你不用去实现自己的锁了.但是你仍然需要去了解怎样使用这些锁. 一个简单的锁 让我们从 java 中的一个同步块开始: public class Counter{ private int count = 0; public int inc(){ synchronized(this){ return ++count; } } } 可以看到在 inc()方法中有一个 synchronized(th

ASP包含文件方法详解

详解 ASP包含文件方法详解 SSI 指令为用户提供在 Web 服务器处理之前将一个文件的内容插入到另一个文件的方法.ASP 只使用这一机制的 #include 指令.要在 .asp 文件中插入一个文件,使用下面的语法: <!--#include virtual | file ="filename"--> virtual 和 file 关键字指示用来包含该文件的路径的类型,filename 是您想包含的文件的路径和名称. 被包含文件不要求专门的文件扩展名:但是,为被包含文件

Java 多线程实例详解(三)_java

本文主要接着前面多线程的两篇文章总结Java多线程中的线程安全问题. 一.一个典型的Java线程安全例子 public class ThreadTest { public static void main(String[] args) { Account account = new Account("123456", 1000); DrawMoneyRunnable drawMoneyRunnable = new DrawMoneyRunnable(account, 700); Thr

Java 多线程实例详解(二)_java

本文承接上一篇文章<Java多线程实例详解(一)>. 四.Java多线程的阻塞状态与线程控制 上文已经提到Java阻塞的几种具体类型.下面分别看下引起Java线程阻塞的主要方法. 1.join() join -- 让一个线程等待另一个线程完成才继续执行.如A线程线程执行体中调用B线程的join()方法,则A线程被阻塞,知道B线程执行完为止,A才能得以继续执行. public class ThreadTest { public static void main(String[] args) {