C# 多线程系列(三)

线程池

创建线程需要时间,如果有不同的小任务要完成,就可以事先创建许多线程,在应完成这些任务时发出请求。这个线程数最好在需要更多线程时增加,在需要释放资源时减少。

不需要自己创建这样的一个列表。该列表由ThreadPool类托管。该类会在需要时增加线程池中线程数,直到最大的线程数。

  • 可以指定创建线程池时立即启动的最小线程数,以及线程池中可用的最大线程数。
  • 如果更多的作业要处理,线程池中的线程个数也到了极限,最新的作业就要排队,且必须等待线程完成其作业。
  • 线程池中的线程都是后台线程,不能把入池的线程改为前台线程。
  • 不能给入池的线程设置优先级和名字。
  • 对于COM对象,入池的所有线程都是多线程单元线程。许多COM对象都需要单线程单元线程。
  • 入池的线程只能用于时间比较短的任务。如果线程要一直运行,就应该使用Thread类创建一个线程。
static void Main()
{
    int nWorkerThreads;
    int nCompletionPorThreads;
    ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPorThreads);
    //ThreadPool.SetMaxThreads(500, 500);
    lib.print("Max worker threads : " + nWorkerThreads);
    lib.print("I/O completion threads: " + nCompletionPorThreads);

    for(int i=0; i<10; i++)
    {
        ThreadPool.QueueUserWorkItem(JobForAThread);//将方法排入队列以便执行。 此方法在有线程池线程变得可用时执行。
    }
    Thread.Sleep(3000);
}

static void JobForAThread(object state)
{
    for(int i = 0; i<3; i++)
    {
        Console.WriteLine("loop {0}, running inside pooled thread {1}", i, Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(50);
    }
}

示例应用程序首先要读取工作线程和I/O线程的最大线程数,把这些信息写入控制台中。接着在for循环中,调用Thread.QueueuserWorkItm()方法,递一个WaitCallback型的委托,把JobForAThread()方法赋予线程池中的线程。

线程池收到这个请求后,会从池中选择一个线程,来调用该方法。如果线程池还没有运行,会创建一个线程池,启动第一个线程。如果线程池己经在运行,有一个空闲线程来完成该任务,把该作业传递给这个线程。

 异步委托

创建线程最简单的方式是定义一个委托,然后异步调用它。

委托使用线程池完成异步任务,当没有前台线程运行时,异步委托将直接结束。

static void Main(string[] args)
{
    Action action = new Action(() => 
    {
        for(int i=0; i<100; i++)
        {
            lib.put(".");
            Thread.Sleep(10);
        }
    });

    AsyncCallback calback = (IAsyncResult result) =>
    {
        Thread.Sleep(100);
        lib.print(result.AsyncState);
    };
    var rs = action.BeginInvoke(calback, "Begin Invoke");//如此,便启动了异步委托。下面用不同方法等待异步委托完成。


    //方法一,用EndInvoke方法,该方法会一直等待,直到委托完成任务为止。
    action.EndInvoke(rs);

    //方法二,使用IAsyncResult相关联的等待句柄。使用AsyncWaitHandle属性可以访问等待句柄。
    //这个属性返回一个WaitHandle类型对象,它可以等待委托线程完成其任务。参数是最长等待时间,
    //-1表示无限等待。如果当前实例收到信号,则返回为 true;否则为 false。
    rs.AsyncWaitHandle.WaitOne(5000);

    //方法三,不断检查
    while (true)
    {
        if (!rs.IsCompleted)
        {
            Thread.Sleep(50);
        }
        else
        {
            break;
        }
    }

    //因为callback最终是异步线程回调的,所以,如果直接退出,callback将无法打印出Begin Invoke。
    Thread.Sleep(200);
}

 任务

 System.Threading.Tasks包含的类抽象出了线程功能,在后台使用ThreadPool。任务表示应完成的某个单元工作,这个单元工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主线程。使用任务不仅可以获得一个抽象层,还可以对底层线程进行很多控制。

  • 启动任务

可以使用实例化的TaskFactory类,在其中把TaskMethod()方法传递给StarNew()方法,就会立刻启动任务。也可以使用Task类的构造函数。实例化Task对象时,任务不会立即运行,而是指定Created状态。接着调用Task类的Start()方法,来启动任务。使用Task类时,还可以调用TunSynchronously()方法。

static void Main(string[] args)
{
    Task t1 = new Task(DoOnFirst);
    t1.Start();

    TaskFactory tf = new TaskFactory();
    Task t2 = tf.StartNew(DoOnFirst);

    Task t3 = Task.Factory.StartNew(DoOnFirst);

    Task.WaitAll(t1, t2, t3);
}
static void DoOnFirst()
{
    lib.print("Task.CurrentId :" + Task.CurrentId);
    lib.print("-----------");
    Thread.Sleep(1000);
}
  • 连续的任务

连续任务通过在任务上调用ContinueWith()方法类定义。不带TaskContinuationOptions参数,则无论前一个任务是如何结束的,后续任务都启动。也可以用TaskContinuationOptions枚举中的值,来指定连续任务在什么情况下启动。

static object o = new object();
static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    cts.Token.Register(()=>{
        Console.WriteLine("*** token canceled.");
    });

    Task t1 = new Task(DoOnFirst, cts.Token);
    //t1完成的情况下启动t2 
    Task t2 = t1.ContinueWith(DoOnSecond, TaskContinuationOptions.OnlyOnRanToCompletion);
    //t1被取消的情况下启动t3 
    Task t3 = t1.ContinueWith(DoOnThird, TaskContinuationOptions.OnlyOnCanceled);
    try
    {
        t1.Start();
        //cts.Cancel( ); //打开注释,取消了t1,将执行DoOnThird。
    }
    catch
    {
        lib.print("t1.IsCanceled : " + t1.IsCanceled);
    }
    Console.ReadKey();
}
static void DoOnFirst()
{
    lock (o)
    {
        lib.print("Task.CurrentId :" + Task.CurrentId);
        lib.print("-----------"); Thread.Sleep(1000);
    }
}
static void DoOnSecond(Task t)
{
    lock (o)
    {
        lib.print("task " + t.Id + " finished.");
        lib.print("this task id " + Task.CurrentId);
        lib.print("-----------"); Thread.Sleep(1000);
    }
}
static void DoOnThird(Task t)
{
    lock (o)
    {
        lib.print("task " + t.Id + " Canceld.");
        lib.print("this task id " + Task.CurrentId);
        lib.print("-----------"); Thread.Sleep(1000);
    }
}

任务层次结构

任务也可以构成一个层次结构。一个任务启动一个新任务时,就启动了一个父/子层次结构。

任务的结果

任务结束时,可以把一些有用的状态信息写到共享对象中。也可以使用返回结果的任务返回这些信息。

static void Main(string[] args)
{
    Task<int> t1 = new Task<int>((object o)=>{return 111;}, "");
    t1.Start();
    t1.Wait();
    lib.print(t1.Result);

    Task t = new Task(DoParentTask);
    t.Start();
}

 C# 多线程系列(四)

原文地址:https://www.cnblogs.com/wrbxdj/p/8462709.html