C#读写者线程(用AutoResetEvent实现同步)

转载自 http://blog.csdn.net/livelylittlefish/article/details/2735440

本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一、小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正!

C#读写者线程(用AutoResetEvent实现同步)

1. AutoResetEvent简介

通知正在等待的线程已发生事件。无法继承此类。

常用方法简介:

  • AutoResetEvent(bool
    initialState):构造函数,用一个指示是否将初始状态设置为终止的布尔值初始化该类的新实例。

false:无信号,子线程的WaitOne方法不会被自动调用    
true:有信号,子线程的WaitOne方法会被自动调用

  • public bool Reset ():将事件状态设置为非终止状态,导致线程阻止;如果该操作成功,则返回true;否则,返回false。

  • public bool Set
    ():将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false。

对于具有 EventResetMode.AutoReset(包括 AutoResetEvent)的
EventWaitHandle,Set 方法释放单个线程。如果没有等待线程,等待句柄将一直保持终止状态,直到某个线程尝试等待它,或者直到它的 Reset
方法被调用。

对于具有 EventResetMode.ManualReset(包括 ManualResetEvent)的
EventWaitHandle,调用Set 方法将使等待句柄一直保持终止状态,直到它的 Reset 方法被调用。

  • WaitOne方法

当在派生类中重写时,阻止当前线程,直到当前的 WaitHandle
收到信号。

  1. WaitHandle.WaitOne () 当在派生类中重写时,阻止当前线程,直到当前的 WaitHandle 收到信号。 由.NET
    Compact Framework 支持。

  2. WaitHandle.WaitOne(Int32, Boolean)  在派生类中被重写时,阻止当前线程,直到当前的WaitHandle
    收到信号,使用 32 位有符号整数度量时间间隔并指定是否在等待之前退出同步域。由 .NET Compact Framework 支持。

  3. WaitHandle.WaitOne(TimeSpan, Boolean)  在派生类中被重写时,阻止当前线程,直到当前实例收到信号,使用
    TimeSpan 度量时间间隔并指定是否在等待之前退出同步域。

2. 读写者线程例子

本例子中,主线程作为写线程,要对某个数据(本例中是个变量)赋值(即写动作),而读线程则等待写线程每次写完数据发出通知,待读线程收到通知后,将数据读出并显示。

[Csharp] view plaincopyprint?

  1. using System;

  2. using
    System.Collections.Generic;

  3. using
    System.Text;
  4. using
    System.Threading;
  5. namespace
    TestAutoResetEvent

  6. {

  7. ///
    <summary>

  8. ///
    读写者线程

  9. ///
    主线程写,子线程读,且只有将数据写入后,读线程才能将其读出

  10. ///
    </summary>

  11. class Program

  12. {

  13. //写线程将数据写入myData

  14. staticint myData =
    100;
  15. //读写次数

  16. constint readWriteCount = 10;
  17. //false:初始时没有信号

  18. static AutoResetEvent autoResetEvent =
    new AutoResetEvent(false);
  19. staticvoid
    Main(string[] args)

  20. {

  21. //开启一个读线程(子线程)

  22. Thread readerThread = new
    Thread(new
    ThreadStart(ReadThreadProc));

  23. readerThread.Name = "ReaderThread";

  24. readerThread.Start();
  25. for (int i = 1; i <= readWriteCount; i++)

  26. {

  27. Console.WriteLine("MainThread writing :
    {0}", i);
  28. //主(写)线程将数据写入

  29. myData = i;
  30. //主(写)线程发信号,说明值已写过了

  31. //即通知正在等待的线程有事件发生

  32. autoResetEvent.Set();
  33. Thread.Sleep(0);

  34. }
  35. //终止线程

  36. readerThread.Abort();

  37. }
  38. staticvoid ReadThreadProc()

  39. {

  40. while (true)

  41. {

  42. //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)

  43. autoResetEvent.WaitOne();

  44. Console.WriteLine("{0} reading : {1}",
    Thread.CurrentThread.Name, myData);

  45. }

  46. }

  47. }

  48. }<pre></pre>

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
///
/// 读写者线程
/// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出
///
class Program
{
//写线程将数据写入myData
static int myData = 100;

//读写次数
const int readWriteCount = 10;

//false:初始时没有信号
static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

static void Main(string[] args)
{
//开启一个读线程(子线程)
Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
readerThread.Name = "ReaderThread";
readerThread.Start();

for (int i = 1; i <= readWriteCount; i++)
{
Console.WriteLine("MainThread writing : {0}", i);

//主(写)线程将数据写入
myData = i;

//主(写)线程发信号,说明值已写过了
//即通知正在等待的线程有事件发生
autoResetEvent.Set();

Thread.Sleep(0);
}

//终止线程
readerThread.Abort();
}

static void ReadThreadProc()
{
while (true)
{
//在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)
autoResetEvent.WaitOne();
Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
}
}
}
}


运行结果如下:

由运行结果可以看出,写线程写入的数据有丢失,主要原因是写线程没有给读线程留足够的时间去进行读操作。

3. 对1进行修改

将主线程睡眠时间改为非0值,观察运行结果。

[Csharp] view plaincopyprint?

  1. using System;

  2. using
    System.Collections.Generic;

  3. using
    System.Text;
  4. using
    System.Threading;
  5. namespace
    TestAutoResetEvent

  6. {

  7. ///
    <summary>

  8. ///
    读写者线程

  9. ///
    主线程写,子线程读,且只有将数据写入后,读线程才能将其读出

  10. ///
    </summary>

  11. class Program

  12. {

  13. //写线程将数据写入myData

  14. staticint myData =
    100;
  15. //读写次数

  16. constint readWriteCount = 10;
  17. //false:初始时没有信号

  18. static AutoResetEvent autoResetEvent =
    new AutoResetEvent(false);
  19. staticvoid
    Main(string[] args)

  20. {

  21. //开启一个读线程(子线程)

  22. Thread readerThread = new
    Thread(new
    ThreadStart(ReadThreadProc));

  23. readerThread.Name = "ReaderThread";

  24. readerThread.Start();
  25. for (int i = 1; i <= readWriteCount; i++)

  26. {

  27. Console.WriteLine("MainThread writing :
    {0}", i);
  28. //主(写)线程将数据写入

  29. myData = i;
  30. //主(写)线程发信号,说明值已写过了

  31. //即通知正在等待的线程有事件发生

  32. autoResetEvent.Set();
  33. Thread.Sleep(1);

  34. }
  35. //终止线程

  36. readerThread.Abort();

  37. }
  38. staticvoid ReadThreadProc()

  39. {

  40. while (true)

  41. {

  42. //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)

  43. autoResetEvent.WaitOne();

  44. Console.WriteLine("{0} reading : {1}",
    Thread.CurrentThread.Name, myData);

  45. }

  46. }

  47. }

  48. }

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
///
/// 读写者线程
/// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出
///
class Program
{
//写线程将数据写入myData
static int myData = 100;

//读写次数
const int readWriteCount = 10;

//false:初始时没有信号
static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

static void Main(string[] args)
{
//开启一个读线程(子线程)
Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
readerThread.Name = "ReaderThread";
readerThread.Start();

for (int i = 1; i <= readWriteCount; i++)
{
Console.WriteLine("MainThread writing : {0}", i);

//主(写)线程将数据写入
myData = i;

//主(写)线程发信号,说明值已写过了
//即通知正在等待的线程有事件发生
autoResetEvent.Set();

Thread.Sleep(1);
}

//终止线程
readerThread.Abort();
}

static void ReadThreadProc()
{
while (true)
{
//在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)
autoResetEvent.WaitOne();
Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
}
}
}
}


运行结果如下:

有结果可知,当主线程睡眠时间大于0值时,读线程即有足够的时间读取写线程写入的数据。这个睡眠时间的长短可以根据实际应用中子线程的计算量设定。

4. 对1再进行修改

主线程在写完数据后根本不睡吗呢?这个时候会发生什么事情?

[Csharp] view plaincopyprint?

  1. using System;

  2. using
    System.Collections.Generic;

  3. using
    System.Text;
  4. using
    System.Threading;
  5. namespace
    TestAutoResetEvent

  6. {

  7. ///
    <summary>

  8. ///
    读写者线程

  9. ///
    主线程写,子线程读,且只有将数据写入后,读线程才能将其读出

  10. ///
    </summary>

  11. class Program

  12. {

  13. //写线程将数据写入myData

  14. staticint myData =
    100;
  15. //读写次数

  16. constint readWriteCount = 10;
  17. //false:初始时没有信号

  18. static AutoResetEvent autoResetEvent =
    new AutoResetEvent(false);
  19. staticvoid
    Main(string[] args)

  20. {

  21. //开启一个读线程(子线程)

  22. Thread readerThread = new
    Thread(new
    ThreadStart(ReadThreadProc));

  23. readerThread.Name = "ReaderThread";

  24. readerThread.Start();
  25. for (int i = 1; i <= readWriteCount; i++)

  26. {

  27. Console.WriteLine("MainThread writing :
    {0}", i);
  28. //主(写)线程将数据写入

  29. myData = i;
  30. //主(写)线程发信号,说明值已写过了

  31. //即通知正在等待的线程有事件发生

  32. autoResetEvent.Set();
  33. //Thread.Sleep(1);

  34. }
  35. //终止线程

  36. readerThread.Abort();

  37. }
  38. staticvoid ReadThreadProc()

  39. {

  40. while (true)

  41. {

  42. //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)

  43. autoResetEvent.WaitOne();

  44. Console.WriteLine("{0} reading : {1}",
    Thread.CurrentThread.Name, myData);

  45. }

  46. }

  47. }

  48. }<pre></pre>

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
///
/// 读写者线程
/// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出
///
class Program
{
//写线程将数据写入myData
static int myData = 100;

//读写次数
const int readWriteCount = 10;

//false:初始时没有信号
static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

static void Main(string[] args)
{
//开启一个读线程(子线程)
Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
readerThread.Name = "ReaderThread";
readerThread.Start();

for (int i = 1; i <= readWriteCount; i++)
{
Console.WriteLine("MainThread writing : {0}", i);

//主(写)线程将数据写入
myData = i;

//主(写)线程发信号,说明值已写过了
//即通知正在等待的线程有事件发生
autoResetEvent.Set();

//Thread.Sleep(1);
}

//终止线程
readerThread.Abort();
}

static void ReadThreadProc()
{
while (true)
{
//在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)
autoResetEvent.WaitOne();
Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
}
}
}
}

运行结果如下:

有结果可知,不睡眠的情况和睡眠时间为0(即Thread.Sleep(0);)效果产不多,只是不睡眠丢失的数据更多了。

5. 对1再修改

将传递给AutoResetEvent的构造函数的参数设置为true,观察运行结果。

[Csharp] view plaincopyprint?

  1. using System;

  2. using
    System.Collections.Generic;

  3. using
    System.Text;
  4. using
    System.Threading;
  5. namespace
    TestAutoResetEvent

  6. {

  7. ///
    <summary>

  8. ///
    读写者线程

  9. ///
    主线程写,子线程读,且只有将数据写入后,读线程才能将其读出

  10. ///
    </summary>

  11. class Program

  12. {

  13. //写线程将数据写入myData

  14. staticint myData =
    100;
  15. //读写次数

  16. constint readWriteCount = 10;
  17. //false:初始时没有信号

  18. static AutoResetEvent autoResetEvent =
    new AutoResetEvent(true);
  19. staticvoid
    Main(string[] args)

  20. {

  21. //开启一个读线程(子线程)

  22. Thread readerThread = new
    Thread(new
    ThreadStart(ReadThreadProc));

  23. readerThread.Name = "ReaderThread";

  24. readerThread.Start();
  25. for (int i = 1; i <= readWriteCount; i++)

  26. {

  27. Console.WriteLine("MainThread writing :
    {0}", i);
  28. //主(写)线程将数据写入

  29. myData = i;
  30. //主(写)线程发信号,说明值已写过了

  31. //即通知正在等待的线程有事件发生

  32. autoResetEvent.Set();
  33. Thread.Sleep(0);

  34. }
  35. //终止线程

  36. readerThread.Abort();

  37. }
  38. staticvoid ReadThreadProc()

  39. {

  40. while (true)

  41. {

  42. //在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)

  43. autoResetEvent.WaitOne();

  44. Console.WriteLine("{0} reading : {1}",
    Thread.CurrentThread.Name, myData);

  45. }

  46. }

  47. }

  48. }<pre></pre>

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace TestAutoResetEvent
{
///
/// 读写者线程
/// 主线程写,子线程读,且只有将数据写入后,读线程才能将其读出
///
class Program
{
//写线程将数据写入myData
static int myData = 100;

//读写次数
const int readWriteCount = 10;

//false:初始时没有信号
static AutoResetEvent autoResetEvent = new AutoResetEvent(true);

static void Main(string[] args)
{
//开启一个读线程(子线程)
Thread readerThread = new Thread(new ThreadStart(ReadThreadProc));
readerThread.Name = "ReaderThread";
readerThread.Start();

for (int i = 1; i <= readWriteCount; i++)
{
Console.WriteLine("MainThread writing : {0}", i);

//主(写)线程将数据写入
myData = i;

//主(写)线程发信号,说明值已写过了
//即通知正在等待的线程有事件发生
autoResetEvent.Set();

Thread.Sleep(0);
}

//终止线程
readerThread.Abort();
}

static void ReadThreadProc()
{
while (true)
{
//在数据被写入前,读线程等待(实际上是等待写线程发出数据写完的信号)
autoResetEvent.WaitOne();
Console.WriteLine("{0} reading : {1}", Thread.CurrentThread.Name, myData);
}
}
}
}

运行结果如下:

若将主线程的睡眠时间改为任意非0值,其运行结果均为下图所示的结果。

                         

6. 其他修改

将主线程调用AutoResetEvent对象的Set方法删除,分别对AutoResetEvent的构造函数的参数为false和true观察运行结果。

为false,运行结果如下图所示。

为true,运行结果如下图所示。

至此,我想我们应该明白AutoResetEvent构造函数的参数的意义了。
false:无信号,子线程的WaitOne方法不会被自动调用; true:有信号,子线程的WaitOne方法会被自动调用。

C#读写者线程(用AutoResetEvent实现同步),布布扣,bubuko.com

时间: 2024-11-03 22:36:38

C#读写者线程(用AutoResetEvent实现同步)的相关文章

C#读写者线程(用AutoResetEvent实现同步)(转载)

C#读写者线程(用AutoResetEvent实现同步) 1. AutoResetEvent简介 通知正在等待的线程已发生事件.无法继承此类. 常用方法简介: AutoResetEvent(bool initialState):构造函数,用一个指示是否将初始状态设置为终止的布尔值初始化该类的新实例.    false:无信号,子线程的WaitOne方法不会被自动调用    true:有信号,子线程的WaitOne方法会被自动调用 public bool Reset ():将事件状态设置为非终止状

C# 线程、任务和同步

1,线程概述线程是程序汇中独立的指令流.线程有一个优先级,实际上正在处理的程序的位置计数器,一个存储其局部变量的栈.每个线程都有自己的栈.但应用程序的内存和堆由一个进程的所有线程共享.进程包含资源,如windows句柄,文件句柄或其他内核对象.每个进程都分配了虚拟内存.一个进程至少包含一个线程.操作系统会调度线程. 总结:同步代码区域(代码块):lock,  Monitor, SpinLock, Mutex,WaitHandle,Semaphore,EventWaitHandle,AutoRes

java 线程之对象的同步和异步

一.多线程环境下的同步与异步 同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去. package com.jalja.org.thread.demo01; public class Thread02 { public synchronized void method1(){ System.out.println("method1:"+Thread.currentThread().getName()); try { T

线程安全、数据同步之synchronized与Lock

线程安全.数据同步之synchronized与Lock 技术交流1群:46523908 技术交流2群:46505645 NoHttp 源码及Demo托管在Github欢迎大家Star:https://github.com/Y0LANDA/NoHttp NoHttp是专门做Android网络请求与下载的框架. 本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解.

Java并发——线程间通信与同步技术

传统的线程间通信与同步技术为Object上的wait().notify().notifyAll()等方法,Java在显示锁上增加了Condition对象,该对象也可以实现线程间通信与同步.本文会介绍有界缓存的概念与实现,在一步步实现有界缓存的过程中引入线程间通信与同步技术的必要性.首先先介绍一个有界缓存的抽象基类,所有具体实现都将继承自这个抽象基类: public abstract class BaseBoundedBuffer<V> { private final V[] buf; priv

线程安全、数据同步之 synchronized 与 Lock

本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解. 为什么要保证线程安全/数据同步 当多个子线程访问同一块数据的时候,由于非同步访问,所以数据可能被同时修改,所以这时候数据不准确不安全. 现实生活中的案例 假如一个银行帐号可以存在多张银行卡,三个人去不同营业点同时往帐号存钱,假设帐号原来有100块钱,现在三个人每人存钱100块,我们最后的结果应该是100 +

java线程研究---(8)Thread同步:锁的概念

(多线程数据共用的)示例代码: 先来看看"java线程研究---(7)Thread同步:多线程数据共用会产生问题"这篇文章里面主要的代码例子 ShareDataThread.java package thread; public class ShareDataThread implements Runnable { private int i = 0; @Override public void run() { while (i < 10) { i++; for (int j =

JAVA多线程提高二:传统线程的互斥与同步&amp;传统线程通信机制

本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥?可以想银行取款的问题,如果不做监控,多个人同时针对一个存折取钱的时候就会出现钱不对的问题,下面我们通过两个例子来分析一下线程的互斥问题以及为什么会产生这个线程? 例子1:一个人生产信息,一个人消费信息 面向对象的思想:类 信息类 生产者 消费者 public class TriditionalTh

GIL 线程池 进程池 同步 异步

1.GIL(理论 重点)2.线程池 进程池3.同步 异步 GIL 是一个全局解释器锁,是一个互斥锁 为了防止竞争解释器资源而产生的 为何需要gil:因为一个python.exe进程中只有一份解释器,如果这个进程开启了多个线程 都要执行代码 多线程之间要竞争解释器 一旦竞争就有可能出现问题 带来的问题:同一时间只有一个线程可以访问解释器 好处:保证了多线程的数据安全 thread-safe 线程安全的 多个线程同时访问也不会出问题 not thread-safe 非线程安全的 多个线程同时访问可能