美文网首页
线程Thread/Task

线程Thread/Task

作者: wwmin_ | 来源:发表于2019-10-10 16:49 被阅读0次

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
  或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
  线程安全问题都是由全局变量及静态变量引起的。
  若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时对一个变量执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

lock的目的是防止多线程执行的时候出现并发操作问题,加上lock的引用类型的对象,在其锁定的区域内,在一个时刻只允许一个线程操作。

lock只能锁定一个引用类型变量,也就是锁定一个地址

 class Program
    {
        static void Main(string[] args)
        {
            ThreadA t = new ThreadA();

            #region Thread
            ThreadA.obj.i = 10;
            Thread th1 = new Thread(new ThreadStart(t.hhh));
            th1.Name = "Th1";
            th1.Start();

            Thread th2 = new Thread(new ThreadStart(t.hhh));
            th2.Name = "Th2";
            th2.Start();
            #endregion

            #region Task
            ThreadA.obj.i = 10;
            Task t1 = new Task(t.hhh);
            t1.Start();

            Task t2 = new Task(t.hhh);
            t2.Start();
            #endregion

            Console.WriteLine("Hello World!");
            Console.WriteLine();
            Console.ReadKey();
        }
    }

    class ThreadA
    {
        public static IntI obj = new IntI();
        public void hhh()
        {
            lock (obj)
            {
                for (int i = 0; i < 7; i++)
                {
                    Thread.Sleep(500);
                    if (obj.i > 0)
                    {
                        obj.i--;
                        Console.WriteLine("当前线程名: " + Thread.CurrentThread.ManagedThreadId+ ",obj.i= " + obj.i);
                    }
                }
            }
        }
    }

    class IntI
    {
        public int i;
    }

加锁和不加锁运行的结果有区别 :

加锁后:i的值会一个个递减,不会出现跳跃,不会出现重复输出,一直到0值;

不加锁:i的值输出会出现跳跃,不连续递减,可能还会出现-1值输出;

原因:加锁后,一个时刻只能有一个线程执行被锁区域的代码,两个线程都是有先后顺序执行的,所以不会出现间断输出。

Task是用来实现多线程的类,在以前当版本中已经有了Thread及ThreadPool,为什么还要提出Task类呢,这是因为直接操作Thread及ThreadPool,向线程中传递参数,获取线程的返回值及线程当启停都非常的麻烦,所以微软的工程师对Thread进行了再封装,这就是Task,可以这么说Task是架构在Thread之上的,

所以多线程时Task是我们的首选。

Task类和Task<TResult>类,后者是前者的泛型版本。TResult类型为Task所调用方法的返回值。

主要区别在于Task构造函数接受的参数是Action委托,而Task<TResult>接受的是Func<TResult>委托

  • Task的声明

Task的声明有两种方式:
a,通过new 的方式来声明

Task objTask = new Task();

b.通过Task.Factory.StartNew的方式来声明

Task.Factory.StartNew(MyMethod);

这两种声明方式的区别,第一种声明方式开启线程必须使用objTask.Start(),而通过Task.Factory.StartNew的方式则不用。

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("调用主线程");
            //Task objTask = new Task(() => Console.WriteLine("Task1"));
            //objTask.Start();
            //new Task(() => Console.WriteLine("Task2")).Start();

            //Task t1 = Task.Run(() => { Thread.Sleep(3000); Console.WriteLine("Task3"); });
            //t1.Wait();
            //Task t2 = Task.Run<string>(() => "wwmin");
            //bool b2 = t2.Wait(2000);
            //Thread.Sleep(4000);

            #region getWaiter and continueWith
            Task<int> TaskInt1 = Task.Run<int>(() =>
            {
                Thread.Sleep(2000);
                return Enumerable.Range(1, 100).Sum();
            });
            var awaiter = TaskInt1.GetAwaiter();
            awaiter.OnCompleted(() =>
            {
                Console.WriteLine("TaskInt1 finished");
                int result = awaiter.GetResult();
                Console.WriteLine(result);
            });
            TaskInt1.ContinueWith(antecedent =>
            {
                Console.WriteLine(antecedent.Result);
                Console.WriteLine("Running continue Task");
            });
            #endregion

            Task<string> s = TestAsync();
            Console.WriteLine(s.Result);
            Console.WriteLine("Hello World!");
            Console.ReadKey();
        }

        static async Task<string> TestAsync()
        {
            Console.WriteLine("运行task之前" + Thread.CurrentThread.ManagedThreadId);
            Task<string> t = Task.Run<string>(() =>
            {
                Console.WriteLine("运行Task" + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(2000);
                return "我是测试线程";
            });
            Console.WriteLine("运行Task之后" + Thread.CurrentThread.ManagedThreadId);
            var result = await t;
            return result;
        }
    }

延迟任务
Task.Delay()方法是相当于异步的Thread.Sleep();

要获得返回值,就要用到Task的泛型版本了。 说到Task的返回值就不得不说await和async关键字了。

当函数使用async标记后,返回值必须为void,Task,Task<T>,当返回值为Task<T>时,函数内部只需要返回T类型,编译器会自动包装成Task<T>类型

await关键字必须在具有async标记的函数内使用。
(1) 在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别(就是说async和await是成对出现的,没有await的async是没有意义的)
(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。
(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。

延续任务,就是说在任务执行完成之后继续执行任务,有两种方法
第一种,使用一种是使用GetAwaiter方法。GetAwaiter方法返回一个TaskAwaiter结构,该结构有一个OnCompleted事件,只需对OnCompleted事件赋值,即可在完成后调用该事件。

相关文章

  • Java多线程

    线程池 线程 Task 一个线程总是关联这一个Task。 拿Thread来说,要么继承Thread(Thread实...

  • 线程Thread/Task

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果...

  • 第21章 线程

    使用Thread类可以创建和控制线程。 基础应用 task与thread的异同(答案收集于百度): task是根据...

  • 8月总结上

    1.task.executeOnExecutor线程池和THREAD_POOL_EXECUTOR一起使用task....

  • Java 线程(1)- 创建与销毁

    Java 采用 thread-per-task 的线程模型,即一个任务(一段代码)对应一个 Java 线程(thr...

  • Unity实践—多线程任务队列实现

    Unity 已可使用 Thread、Task 等处理多线程任务,但缺少成熟的多线程任务队列工具,所以在此实现一个,...

  • .NET多线程(Thread,ThreadPool,Task,A

    .NET多线程是什么? 进程与线程 进程是一种正在执行的程序。 线程是程序中的一个执行流。 多线程是指一个程序中可...

  • C# 多线程的使用

    此篇文章简单总结了C#中主要的多线程实现方法,包括Thread、ThreadPool、Parallel和Task类...

  • C# Task

    Task是一种基于任务的编程模型。它与thread的主要区别是,它更加方便对线程进程调度和获取线程的执行结果。 T...

  • 多线程知识点记录

    c# Thread、ThreadPool、Task有什么区别,什么时候用,以及Task的使用先说 Thread与T...

网友评论

      本文标题:线程Thread/Task

      本文链接:https://www.haomeiwen.com/subject/tcivuctx.html