C# 多线程编程 线程的创建方式

Thread

C#中,Thread类位于System.Threading空间下,当new一个Thread实例时,并不会实际创建出一个操作系统层次的线程,只有当调用Start方法时,才会实际创建出操作系统线程,然后操作系统线程执行C#线程对应的回调函数。

using System;
using System.Threading;

namespace MultiThread
{
    
    
    internal class Program
    {
    
    
        public static void Main(string[] args)
        {
    
    
            Console.WriteLine("主线程中开启一个专用线程");
            Thread thread = new Thread(ComplexCompute);
            thread.Start(6);
            Console.WriteLine("主线程做其他的工作");
            Thread.Sleep(10000);
            Console.WriteLine("请输入Enter结束");
            Console.ReadLine();
        }
        
        public static void ComplexCompute(object count)
        {
    
    
            int i = 1;
            while (i < (int)count)
            {
    
    
                Console.WriteLine("专用线程计时:{0}", i);
                Thread.Sleep(1000);
                i++;
            }
        }
    }
}

通过观察任务管理器,可以发现,当没有ComplexCompute这个线程时,一个控制台应用程序会使用6个线程。在这里插入图片描述
当开启ComplexCompute这个线程时,线程数会增加1。
在这里插入图片描述

ThreadPool

除了Thread类外,在CLR中,也可以使用ThreadPool来使用一个线程池中的线程,用来处理异步操作,在上面代码中,只需要将

Thread thread = new Thread(ComplexCompute);
thread.Start(6);

替换为

ThreadPool.QueueUserWorkItem(ComplexCompute, 6);

即可。
此时,线程数量为8。
在这里插入图片描述

Thread的协作式取消

在主线程中创建一个CancellationTokenSource实例,将他的CancellationToken属性作为参数传给专用线程的回调函数,在函数中,判断主线程中是否有取消操作,若有,则返回,从而终止专用线程。

using System;
using System.Threading;

namespace MultiThread
{
    
    
    internal class Program
    {
    
    
        public static void Main(string[] args)
        {
    
    
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            Console.WriteLine("主线程中开启一个专用线程");
            ThreadPool.QueueUserWorkItem(o => ComplexCompute(cancellationTokenSource, 6));
            Console.WriteLine("请输入Enter来终止专用线程");
            Console.ReadLine();
            cancellationTokenSource.Cancel();
            Console.WriteLine("专用线程取消成功");
            Console.ReadLine();
        }

        public static void ComplexCompute(CancellationTokenSource cts, object count)
        {
    
    
            for (int i = 1; i < (int) count; i++)
            {
    
    
                if (cts.IsCancellationRequested)
                {
    
    
                    Console.WriteLine("请求取消专用线程");
                    break;
                }

                Console.WriteLine("专用线程计时:{0}", i);
                Thread.Sleep(1000);
            }
        }
    }
}

Task

此外, 还可以通过Task类来开启一个线程,与Thread不同的是,只需要将

Thread thread = new Thread(ComplexCompute);
thread.Start(6);

替换为

// 方式一
Task task = new Task(ComplexCompute, 6);
task.Start();

或者

// 方式二
Task.Run(() => ComplexCompute(6));

即可。
此时,线程数量为11。
在这里插入图片描述
对于第一种方式,可以给Task构造器传递一个Action委托,也可以传递一个Action<Object>委托,如果是后者,需要向构造器传递实参。如果需要返回值,则可以构造Task<TResult>对象,例如

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MultiThread
{
    
    
    internal class Program
    {
    
    
        public static void Main(string[] args)
        {
    
    
            Console.WriteLine("主线程中开启一个专用线程");
            Task<int> task = new Task<int>(ComplexCompute, 6);
            task.Start();
            Console.WriteLine("专用线程计算结果:{0}", task.Result);
            Console.WriteLine("主线程做其他的工作");
            Thread.Sleep(10000);
            Console.WriteLine("请输入Enter结束");
            Console.ReadLine();
        }
        
        public static int ComplexCompute(object count)
        {
    
    
            int i = 1;
            while (i < (int)count)
            {
    
    
                Console.WriteLine("专用线程计时:{0}", i);
                Thread.Sleep(1000);
                i++;
            }

            return i;
        }
    }
}

对于第二种方式,调用Run时可以传递一个Action或者Func<TResult>委托,来执行想要的操作。
对于构造器或者Run方式,都可以选择性传递一个CancellationToken,用来协作式取消线程。

Task的协作式取消

Thread或者ThreadPool开启线程不同的是,Task可以有返回值。在取消一个任务时,为了区别Task.Result是被取消还是正常返回值,可以使用CancellationToken.ThrowIfCancellationRequested函数,与CancellationToken.IfCancellationRequested相同的是会终止Task,不同的是会抛出一个OperationCanceledException,从而在使用Task.Result时会抛出AggregateException。AggregateExceptionTask产生异常时得到的一个集合,可以使用Handle来处理其中的异常, 例如:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MultiThread
{
    
    
    internal class Program
    {
    
    
        public static void Main(string[] args)
        {
    
    
            Console.WriteLine("主线程中开启一个专用线程");
            CancellationTokenSource cts = new CancellationTokenSource();
            Task<int> task = Task.Run(() => ComplexCompute(cts.Token, 6));
            Console.WriteLine("请输入Enter取消专用线程");
            Console.ReadLine();
            cts.Cancel();
            try
            {
    
    
                Console.WriteLine("专用线程计算结果:{0}", task.Result);
            }
            catch (AggregateException e)
            {
    
    
                e.Handle(c => c is OperationCanceledException);
            }
            Console.WriteLine("请输入Enter结束");
            Console.ReadLine();
        }
        
        public static int ComplexCompute(CancellationToken token, object count)
        {
    
    
            int i = 1;
            while (i < (int)count)
            {
    
    
                token.ThrowIfCancellationRequested();
                Console.WriteLine("专用线程计时:{0}", i);
                Thread.Sleep(1000);
                i++;
            }

            return i;
        }
    }
}

问题

  • 对于同一个操作,ThreadThreadPoolTask实际开启的线程数分别是1、2和5个,不明白其中的原因;
  • 对于控制台应用程序,其有6个线程,也不知道为什么。

猜你喜欢

转载自blog.csdn.net/Abecedarian_CLF/article/details/104251257