C#基于任务的异步编程基础(一)之Task管理多线程

在C#4.0以后,可以使用System.Threading.Tasks下的Task类来创建和管理多线程,下面将记录如何简单的使用Task.

一、使用Task执行静态方法

在控制台编写如下下面:

        static void Main(string[] args)
        {

            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 主线程id:" + tid);
            Task.Run(() => PrintName("小明"));


            Console.ReadKey();
        }

        private static void PrintName(string name)
        {
            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);

        }

如上代码即展示了最简单的创建一个线程执行一个方法的过程,从执行结果可以看出主线程和Task创建的线程。

二、使用Task执行非静态方法

Task创建一个线程执行静态方法固然简单,但是不能用同样的方法执行非静态方法,是因为非静态方法所属的类必须要实例化,所以Task创建线程执行非静态方法有点区别,如下代码将展示如何编写代码:

1.在控制台中新增一个方法:

        public void PrintName2(string name)
        {
            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
        }

2.控制台Main方法中编写如下代码:

        static void Main(string[] args)
        {

            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 主线程id:" + tid);
            Task.Run(() => PrintName("小明"));
            Task task = new Task(() => new Program().PrintName2("小花"));
            task.Start();


            Console.ReadKey();
        }

注意:Task执行非静态方法必须需要实例化非静态方法的类,并且调用Task的Start方法才能正确执行。

三、Task执行具有返回值的方法

上述代码中展示了如何创建线程执行静态方法和非静态方法,并且传递了一个参数,那么下面将展示如何执行带有返回值的方法:

1.将如上的PrintName代码更改成带有返回值的方法,如下:

        private static string PrintName(string name)
        {
            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
            return name;
        }

2.在控制台中使用Result即可获得返回值,即

            var task = Task.Run(() => PrintName("小明"))    

            Console.WriteLine(DateTime.Now.ToString() + " 主线程id:" + tid + "返回值:" + task.Result);

3.假如返回的是一个结构或者类呢?

1)创建一个Person类,代码如下:

    public class Person
    {
        private string _name = "";
        public void SetName(string name)
        {
            _name = name;
        }
        public string GetName()
        {
            return _name;
        }

    }

2)将控制台中的PrintName更改成返回一个Person对象,代码如下:

        private static Person PrintName(string name)
        {
            Person person = new Person();
            person.SetName(name);
            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
            return person;
        }

3)控制台中这样编写即可:

var task = Task.Run(() => PrintName("小明"));

并且此时task就是一个Person对象,直接可以使用如task.Result.GetName()来获取对象的方法。

四、TaskFactory使用范例

在System.Threading.Tasks命名空间下有一个TaskFactory类,使用该类同样可以创建现在执行方法,注意的是,这个类可以单独使用也可以在Task类下使用,两种使用方法如下:

            Task.Factory.StartNew(()=> PrintName("小王"));


            TaskFactory factory = new TaskFactory();
            factory.StartNew(() => PrintName("小张"));
            factory.StartNew(() => PrintName("小赵"));

五、高频率多次使用Task创建线程执行方法注意事项

假如如下代码循环多次执行PrintName方法,将会出现什么情况呢?

        static void Main(string[] args)
        {

            for (int i = 0; i < 10; i++)
            {
                Task.Run(() => PrintName("小明" + i));
            }
            Console.ReadKey();
        }

        private static void PrintName(string name)
        {
            string tid = Thread.CurrentThread.ManagedThreadId.ToString();//当前线程id
            Console.WriteLine(DateTime.Now.ToString() + " 线程id:" + tid + " Name:" + name);
        }

运行结果如图:

C#基于任务的异步编程基础(一)之Task管理多线程_第1张图片

从结果看,很明显不是控制台中代码的意图,控制台中代码的意图是想,每次循环希望调用的方法的参数将循环变量添加进去,为何结果是这样的呢?假如需要达到真实的意图,即把代码改成:

                string name = "小明" + i;
                Task.Run(() => PrintName(name));

这样既可,那么为何不能直接在Task执行的方法参数中拼接,两种拼接的区别又在哪?

1.string name = "小明" + i;这样的编写,每次传递的是一个固定的字符串的引用的值,所以运行结果就是每次执行方法传递的参数值是固定且不同的。

2.Task.Run(() => PrintName("小明" + i));这种写法是,每次都传递了一个变量,这个变量的在Task执行方法的时候的值并不是循环体循环时候的值,本次执行结果都是10,是因为for循环极快,10次都循环完了,第一个Task的方法还没执行完毕,假如调试加个断点或者阻塞主进程一会,输出的将不会都是10.

综上所述,在Task创建线程执行方法时候,假如方法有参数,参数一定不能是变量,否则结果会事与愿违。

 

 

你可能感兴趣的:(C#基础学习,C#,多线程,Task)