C#笔记 线程

草稿箱告急,半成品也先发一下吧。。。

1. 线程

线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。

下面列出了线程生命周期中的各种状态:

  • 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
  • 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
  • 不可运行状态:下面的几种情况下线程是不可运行的:
    • 已经调用 Sleep 方法
    • 已经调用 Wait 方法
    • 通过 I/O 操作阻塞
  • 死亡状态:当线程已完成执行或已中止时的状况。

在 C# 中,System.Threading.Thread 类用于线程的工作。它允许创建并访问多线程应用程序中的单个线程。进程中第一个被执行的线程称为主线程。当程序开始执行时,主线程自动创建。使用 Thread 类创建的线程被主线程的子线程调用。可以使用 Thread 类的 CurrentThread 属性访问线程。

示例:

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class MainThreadProgram
    {
        static void Main(string[] args)
        {
            Thread th = Thread.CurrentThread;
            th.Name = "MainThread";
            Console.WriteLine("This is {0}", th.Name);
            Console.ReadKey();
        }
    }
}

输出

This is MainThread

 

2. Lock

    lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断。它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。这是通过在代码块运行期间为给定对象获取互斥锁来实现的。在多线程中,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。

在C# lock关键字定义如下:

    lock(expression) statement_block

其中expression代表你希望跟踪的对象,通常是对象引用。如果你想保护一个类的实例,一般地,你可以使用this;如果你想保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。而statement_block就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。

示例:

using System;
using System.Threading;

namespace ThreadDemo
{
    class Program
    {
        private static object myLockObject = new object();
        public static void fun() 
        {
            lock (myLockObject) 
            {
                int i = 0;
                while (i < 1000)
                {
                    Console.WriteLine("In {0},i={1}", Thread.CurrentThread.Name,i);
                    i++;
                } 
            }        
        }
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "MainThread";
            Thread childthread = new Thread(new ThreadStart(fun));
            childthread.Name = "ChildThread";
            Console.WriteLine("In Main: Creating the Child thread");            
            childthread.Start();          
            fun();
        }
    }
}

结果:在主线程执行完再在线程执行,不然一会在主线程一会在子线程。

In Main: Creating the Child thread
In MainThread,i=0
...
In MainThread,i=999
In ChildThread,i=0
...
In ChildThread,i=999

 3. WaitHandle

  |——EventWaitHandle                  事件构造。
    |——AutoResetEvent
    |——ManualResetEvent
  |——Semaphore                         信号量构造。
  |——Mutex                                 互斥体构造。

3.1 EventWaitHandle

  EventWaitHandle是AutoResetEvent和ManualResetEvent的基类,从EventWaitHandle可以创建两者。EventWaitHandle 类用于在异步操作时控制线程间的同步,即控制一个或多个线程继行或者等待其他线程完成。

EventWaitHandle(bool initialState, EventResetMode mode);

initialState设置初始状态,如果为true,则WaitOne()在调用Reset()方法前不会阻塞线程,先调用Reset()再调用WaitOne()会阻塞WaitOne()所在线程;如果设置为false,则WaitOne()会阻塞,直到调用Set()。

mode设置为EventResetMode.ManualReset,在调用Set()时所有WaitOne()阻塞线程都会继续执行;设置为EventResetMode.AutoReset则每次调用Set()会使一个WaitOne()阻塞的线程继续执行,一般按照阻塞先后顺序执行。

  • AutoResetEvent
  当某个线程调用WaitOne方法后,信号处于发送状态,该线程会得到信号, 程序就会继续向下执行,否则就等待。而且 AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程,其他线程还是堵塞。
  • ManualResetEvent 

  当A线程的活动结束后,B线程才能开始的情况下,首先,调用Reset 以将 ManualResetEvent 置于非终止状态(false),此线程(A)可被视为控制 ManualResetEvent。在B线程调用ManualResetEvent 上的WaitOne将线程阻止。当控制线程(A)完成活动时,它调用Set以发出等待线程可以继续进行的信号。ManualResetEvent 将保持终止状态(true),直到它被手动重置(Reset)。

示例:主线程进行i++计算,子线程打印。如果主线程的计算很慢,阻塞子线程打印,可以避免重复打印。

using System;
using System.Threading;

namespace ThreadDemo
{
    class Demo
    {
        static ManualResetEvent printevent, printexit;
        static int i = 0;
        public static void count()
        {
            Thread.Sleep(10);
            i++;
            Console.WriteLine("In thread{0}, i++", Thread.CurrentThread.ManagedThreadId, i);
            printevent.Set();//将事件状态设置为终止状态,允许子线程继续
        }
        public static void print()
        {
            while (true)
            {
                printevent.WaitOne();
                Thread.Sleep(50);
                Console.WriteLine("In thread{0}, i = {1}", Thread.CurrentThread.ManagedThreadId, i);
                printevent.Reset();//将事件状态设置为非终止状态,导致线程阻塞
            }
        }

        static void Main(string[] args)
        {
            printevent = new ManualResetEvent(false);
            Thread childThread = new Thread(print);
            childThread.Start();
            while (i < 1000)
            {
                count();
            }
        }
    }
}

 

示例二:如果是AutoResetEvent,可以省略manualWaitHandler.Reset();如果是ManualResetEvent,不加Reset()的话,WaitOne()无效,直接往下运行了。

ManualResetEvent manualWaitHandler = new ManualResetEvent(false);//false 即非终止,未触发。
            new Thread(() =>
            {
                manualWaitHandler.WaitOne();  //阻塞当前线程对象,等待信号。
                Console.WriteLine("接收到信号,开始处理。");
                manualWaitHandler.Reset();  //手动 设置事件对象状态为非终止状态,false。
manualWaitHandler.WaitOne(); //这里直接阻塞等待无效,因为事件对象还是true,必须手动调reset。 Console.WriteLine("第二次接收到信号,开始处理。"); }).Start(); new Thread(() => { Thread.Sleep(2000); Console.WriteLine("发信号"); manualWaitHandler.Set(); //向事件对象发送ok信号。。 Thread.Sleep(2000); Console.WriteLine("第二次发信号"); manualWaitHandler.Set(); }).Start(); Console.ReadLine();

3.2 Semaphore

3.3 Mutex

上一篇:(二)selenium安装


下一篇:《手把手教你》系列技巧篇(五十三)-java+ selenium自动化测试-上传文件-上篇(详细教程)