zoukankan      html  css  js  c++  java
  • 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# 多线程系列(四)

  • 相关阅读:
    gulp+browser-sync使用方法
    小程序试用体验
    移动端调试总结
    函数防抖和函数分流
    页面返回顶部的方法总结
    POJ
    POJ
    UVA 10129 Play on Words(欧拉道路)
    UVA 10305 Ordering Tasks (拓扑排序)
    UVA 12657 Boxes in a Line(双向链表+小技巧)
  • 原文地址:https://www.cnblogs.com/wrbxdj/p/8462709.html
Copyright © 2011-2022 走看看