归纳一下:C#线程同步的几种方法(2)_Eagle's World_百度空间

//from

  八、MethodImplAttribute

  如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的话,使用MethodImplAttribute属性会更简单一些。这样就不用在方法内部加锁了,只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了,MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。如果要提前释放锁,则应该使用Monitor或lock。我们来看一个例子:

Code
[MethodImpl(MethodImplOptions.Synchronized)]
public void
DoSomeWorkSync()
{
Console.WriteLine(
" DoSomeWorkSync() -- Lock held by Thread " +

Thread.CurrentThread.GetHashCode());
Thread.Sleep(
1000 );
Console.WriteLine(
" DoSomeWorkSync() -- Lock released by Thread " +

Thread.CurrentThread.GetHashCode());
}
public void DoSomeWorkNoSync()
{
Console.WriteLine(
" DoSomeWorkNoSync() -- Entered Thread is " +

Thread.CurrentThread.GetHashCode());
Thread.Sleep(
1000 );
Console.WriteLine(
" DoSomeWorkNoSync() -- Leaving Thread is " +

Thread.CurrentThread.GetHashCode());
}

[STAThread]
static void Main( string [] args)
{
MethodImplAttr testObj
= new
MethodImplAttr();
Thread t1
= new Thread( new
ThreadStart(testObj.DoSomeWorkNoSync));
Thread t2
= new Thread( new
ThreadStart(testObj.DoSomeWorkNoSync));
t1.Start();
t2.Start();
Thread t3
= new Thread( new
ThreadStart(testObj.DoSomeWorkSync));
Thread t4
= new Thread( new
ThreadStart(testObj.DoSomeWorkSync));
t3.Start();
t4.Start();

Console.ReadLine();
}
 

这里,我们有两个方法,我们可以对比一下,一个是加了属性MethodImpl的DoSomeWorkSync(),一个是没加的DoSomeWorkNoSync()。在方法中Sleep(1000)是为了在{dy}个线程还在方法中时,第二个线程能够有足够的时间进来。对每个方法分别起了两个线程,我们先来看一下结果:

可以看出,对于线程1和2,也就是调用没有加属性的方法的线程,当线程2进入方法后,还没有离开,线程1有进来了,这就是说,方法没有同步。我们再来看看线程3和4,当线程3进来后,方法被锁,直到线程3释放了锁以后,线程4才进来。

  九、同步事件和等待句柄

  用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。同步事件是有两个状态(终止和非终止)的对象,它可以用来xx和挂起线程。

  同步事件有两种:AutoResetEventManualResetEvent。它们之间{wy}不同的地方就是在xx线程之后,状态是否自动由终止变为非终止。AutoResetEvent自动变为非终止,就是说一个AutoResetEvent只能xx一个线程。而ManualResetEvent要等到它的Reset方法被调用,状态才变为非终止,在这之前,ManualResetEvent可以xx任意多个线程。

  可以调用WaitOne、WaitAny或WaitAll来使线程等待事件。它们之间的区别可以查看MSDN。当调用事件的 Set方法时,事件将变为终止状态,等待的线程被唤醒。

  来看一个例子,这个例子是MSDN上的。因为事件只用于一个线程的xx,所以使用 AutoResetEventManualResetEvent 类都可以。

Code
static
AutoResetEvent autoEvent;

static void
DoWork()
{
Console.WriteLine(
" worker thread started, now waiting on event"
);
autoEvent.WaitOne();
Console.WriteLine(
" worker thread reactivated, now exiting"
);
}

[STAThread]
static void Main(string
[] args)
{
autoEvent
= new AutoResetEvent(false
);

Console.WriteLine(
"main thread starting worker thread"
);
Thread t
= new Thread(new
ThreadStart(DoWork));
t.Start();

Console.WriteLine(
"main thrad sleeping for 1 second"
);
Thread.Sleep(
1000
);

Console.WriteLine(
"main thread signaling worker thread"
);
autoEvent.Set();

Console.ReadLine();
}
 

我们先来看一下输出:

在主函数中,首先创建一个AutoResetEvent的实例,参数false表示初始状态为非终止,如果是true的话,初始状态则为终止。然后创建并启动一个子线程,在子线程中,通过调用AutoResetEvent的WaitOne方法,使子线程等待指定事件的发生。然后主线程等待一秒后,调用AutoResetEvent的Set方法,使状态由非终止变为终止,重新xx子线程。

参考:

1/MSDN( )

2/http://www.cnblogs.com/VincentWP/archive/2008/06/25/1229104.html

===========================================================

//msdn

以下各节描述了在多线程应用程序中可以用来同步资源访问的功能和类。

在应用程序中使用多个线程的一个好处是每个线程都可以异步执行。对于 Windows 应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应。对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力。否则,在xx满足前一个请求之前,将无法处理每个新请求。

然而,线程的异步特性意味着必须协调对资源(如文件句柄、网络连接和内存)的访问。否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作。结果将产生不可预知的数据损坏。

对于整数数据类型的简单操作,可以用 类的成员来实现线程同步。对于其他所有数据类型和非线程安全的资源,只有使用本主题中的结构才能安全地执行多线程处理。

有关多线程编程的背景信息,请参见:

lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。

lock 语句以关键字 lock 开头,它有一个作为参数的对象,在该参数的后面还有一个一次只能由一个线程执行的代码块。例如:

提供给 lock 关键字的参数必须为基于引用类型的对象,该对象用来定义锁的范围。在上例中,锁的范围限定为此函数,因为函数外不存在任何对该对象的引用。严格地说,提供给 lock 的对象只是用来{wy}地标识由多个线程共享的资源,所以它可以是任意类实例。然而,实际上,此对象通常表示需要进行线程同步的资源。例如,如果一个容器对象将被多个线程使用,则可以将该容器传递给 lock,而 lock 后面的同步代码块将访问该容器。只要其他线程在访问该容器前先锁定该容器,则对该对象的访问将是安全同步的。

通常,{zh0}避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于对象)也可能导致问题。锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。因此,{zh0}锁定不会被暂留的私有或受保护成员。某些类提供专门用于锁定的成员。例如, 类型提供 。许多集合类型也提供 SyncRoot

有关 lock 关键字的更多信息,请参见:

lock 关键字类似,监视器防止多个线程同时执行代码块。 方法允许一个且仅一个线程继续执行后面的语句;其他所有线程都将被阻止,直到执行语句的线程调用 。这与使用 lock 关键字一样。事实上,lock 关键字就是用 类来实现的。例如:

这等效于:

使用 lock 关键字通常比直接使用 Monitor 类更可取,一方面是因为 lock 更简洁,另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放基础监视器。这是通过 finally 关键字来实现的,无论是否引发异常它都执行关联的代码块。

有关监视器的更多信息,请参见。

使用锁或监视器对于防止同时执行区分线程的代码块很有用,但是这些构造不允许一个线程向另一个线程传达事件。这需要“同步事件”,它是有两个状态(终止和非终止)的对象,可以用来xx和挂起线程。让线程等待非终止的同步事件可以将线程挂起,将事件状态更改为终止可以将线程xx。如果线程试图等待已经终止的事件,则线程将继续执行,而不会延迟。

同步事件有两种: 和 。它们之间{wy}的不同在于,无论何时,只要 AutoResetEvent xx线程,它的状态将自动从终止变为非终止。相反,ManualResetEvent 允许它的终止状态xx任意多个线程,只有当它的 方法被调用时才还原到非终止状态。

可以通过调用一种等待方法,如 、 或 ,让线程等待事件。 使线程一直等待,直到单个事件变为终止状态; 阻止线程,直到一个或多个指示的事件变为终止状态; 阻止线程,直到所有指示的事件都变为终止状态。当调用事件的 方法时,事件将变为终止状态。

在下面的示例中,创建了一个线程,并由 Main 函数启动该线程。新线程使用 WaitOne 方法等待一个事件。在该事件被执行 Main 函数的主线程终止之前,该线程一直处于挂起状态。一旦该事件终止,辅助线程将返回。在本示例中,因为事件只用于一个线程的xx,所以使用 AutoResetEventManualResetEvent 类都可以。

using System;
using System.Threading;

class ThreadingExample
{
    static AutoResetEvent autoEvent;

    static void DoWork()
    {
        Console.WriteLine("   worker thread started, now waiting on event...");
        autoEvent.WaitOne();
        Console.WriteLine("   worker thread reactivated, now exiting...");
    }

    static void Main()
    {
        autoEvent = new AutoResetEvent(false);

        Console.WriteLine("main thread starting worker thread...");
        Thread t = new Thread(DoWork);
        t.Start();

        Console.WriteLine("main thrad sleeping for 1 second...");
        Thread.Sleep(1000);

        Console.WriteLine("main thread signaling worker thread...");
        autoEvent.Set();
    }
}

有关线程同步事件用法的更多示例,请参见:

mutex 与监视器类似;它防止多个线程在某一时间同时执行某个代码块。事实上,名称“mutex”是术语“互相排斥 (mutually exclusive)”的简写形式。然而与监视器不同的是,mutex 可以用来使跨进程的线程同步。mutex 由 类表示。

当用于进程间同步时,mutex 称为“命名 mutex”,因为它将用于另一个应用程序,因此它不能通过全局变量或静态变量共享。必须给它指定一个名称,才能使两个应用程序访问同一个 mutex 对象。

尽管 mutex 可以用于进程内的线程同步,但是使用 Monitor 通常更为可取,因为监视器是专门为 .NET Framework 而设计的,因而它可以更好地利用资源。相比之下,Mutex 类是 Win32 构造的包装。尽管 mutex 比监视器更为强大,但是相对于 Monitor 类,它所需要的互操作转换更消耗计算资源。有关 mutex 的用法示例,请参见 。



郑重声明:资讯 【归纳一下:C#线程同步的几种方法(2)_Eagle's World_百度空间】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——