线程系列01,前台线程,后台线程,线程同步

在控制台应用程序集中,Main方法开始的是一个线程。如果要再创建线程,需要用到System.Threading这个命名空间。

□ 创建第一个线程

using System;
using System.Threading;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            var thread = new Thread(DoSth);
            thread.Start();
        }

        static void DoSth()
        {
            Console.WriteLine("我来自另外一个线程");
        }
    }
}


□ 前台线程和后台线程

新建的线程在默认情况下是前台线程,可以通过把IsBackground属性设置为true,把线程定义为后台线程,一旦定义成后台线程,只要前台线程结束,无论后台线程是否结束,应用程序进程结束。

using System;
using System.Threading;


namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            var thread = new Thread(DoSth);
            thread.IsBackground = true;
            thread.Start(1);
            Console.WriteLine("离开主线程");
        }

        static void DoSth(object threadId)
        {
            Console.WriteLine("我来自另外一个线程" + threadId);
        }
    }
}


○ ManagedThreadId属性,托管线程Id,在进程内唯一,与操作系统的线程Id不是一回事。
○ Start方法可以带参数,参数将被传递到线程方法
○ IsBackground属性,设置线程是否为后台线程

□ 线程同步

※ 线程不同步的问题

假设主程序中有一个静态变量,在主程序的方法内无限循环,每次让该静态变量自增1。

如果把该方法交给一个线程。

    class Program
    {
        private static int count = 0;
        static void Main(string[] args)
        {
            var t1 = new Thread(AddCount);
            t1.Start();
        }

        static void AddCount()
        {
            while (true)
            {
                int temp = count;
                Thread.Sleep(1000);
                count = temp + 1;
                Console.WriteLine("我的托管线程ID为:" + Thread.CurrentThread.ManagedThreadId + " 目前count的值为:" + count);
                Thread.Sleep(1000);
            }
        }
    }


运行良好,显示的count值是连续递增1。

如果把该方法交给2个线程。

    class Program
    {
        private static int count = 0;
        static void Main(string[] args)
        {
            var t1 = new Thread(AddCount);
            var t2 = new Thread(AddCount);
            t1.Start();
            t2.Start();
        }

        ......
    }

我们发现,count的值不是递增。也就是说,count的值没有做到同步。

→进入线程1,temp=0,线程1开始sleep
→进入线程2,temp=0,线程2开始sleep
→线程1"醒来",让count=1,显示count值为1,又sleep
→线程2"醒来",temp还是为0,所以count还是为1,显示count值为1,又sleep
→如此循环
这里的问题是:本想让count一直递增,但线程1和线程2没有适时同步。如何解决呢?

※ 让线程同步

使用lock语句块,可以让2个线程同步,让每次只有一个线程进入程序执行的某个部分。

    class Program
    {
        private static int count = 0;
        static object o = new object();
        static void Main(string[] args)
        {
            var t1 = new Thread(AddCount);
            var t2 = new Thread(AddCount);
            t1.Start();
            t2.Start();
        }

        static void AddCount()
        {
            while (true)
            {
                lock (o)
                {
                    int temp = count;
                    Thread.Sleep(1000);
                    count = temp + 1;
                    Console.WriteLine("我的托管线程ID为:" + Thread.CurrentThread.ManagedThreadId + " 目前count的值为:" + count);
                }
                Thread.Sleep(1000);
            }
        }
    }


总结:
○ 如果允许一个主线程结束,其它线程不管执行情况如何都结束,就把其它线程设置为后台线程。
○ lock语句块能保证线程同步

时间: 2024-10-25 14:38:52

线程系列01,前台线程,后台线程,线程同步的相关文章

线程系列06,通过CLR代码查看线程池及其线程

在"线程系列04,传递数据给线程,线程命名,线程异常处理,线程池"中,我们已经知道,每个进程都有一个线程池.可以通过TPL,ThreadPool.QueueUserWorkItem,委托与线程池交互.本篇体验:通过查看CLR代码来观察线程池及其线程. □ 通过编码查看线程池和线程 使用ThreadPool的静态方法QueueUserWorkItem把线程放入线程池,来看线程池线程和主程序线程的执行情况. class Program { static void Main(string[]

线程系列10,无需显式调用线程的情形

通常,我们会通过线程的构造函数先创建线程再使用线程.而实际上,.NET中有些类提供的方法,其内部就是使用多线程处理的.一些封装了多线程.异步处理方法的类都符合了"事件驱动异步模式(event-based asynchronous pattern)".以System.ComponentModel下的BackgroundWorker类来说,该类就符合这种模式. BackgroundWorker类属性:WorkerSupportsCancellation:设置为true表示允许取消Worke

死磕 java线程系列之自己动手写一个线程池

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写的线程池如何测试? 简介 线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文彤哥将手把手带你写一个可用的线程池. 属性分析 线程池,顾名思义它首先是一个"池",这个池里面放的是线程,线程是用来执行任务的. 首先,线程池中的线程应该是有类别的,有的是核心线程,有

死磕 java线程系列之自己动手写一个线程池(续)

(手机横屏看源码更方便) 问题 (1)自己动手写的线程池如何支持带返回值的任务呢? (2)如果任务执行的过程中抛出异常了该怎么处理呢? 简介 上一章我们自己动手写了一个线程池,但是它是不支持带返回值的任务的,那么,我们自己能否实现呢?必须可以,今天我们就一起来实现带返回值任务的线程池. 前情回顾 首先,让我们先回顾一下上一章写的线程池: (1)它包含四个要素:核心线程数.最大线程数.任务队列.拒绝策略: (2)它具有执行无返回值任务的能力: (3)它无法处理有返回值的任务: (4)它无法处理任务

线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.Exit处理数据同步※ 使用Mutex处理进程间数据同步※ 使用Semaphore处理数据同步※ 线程死锁 □ 使用lock处理数据同步 假设有一个类,主要用来计算该类2个字段的商,在计算商的方法之内让被除数自减,即被除数有可能为零.使用lock语句块保证每次只有一个线程进入该方法. class Th

线程系列02,多个线程同时处理一个耗时较长的任务以节省时间

当面对一个耗时较长的任务时,我们可以把这个任务切分成多个部分,然后同时交给多个线程处理. □ 统计字节数组一个比较耗时的方式 以下来统计一个字节数组的大小. class Program { static byte[] values = new byte[500000000]; static void Main(string[] args) { GenerateByteArray(); Console.WriteLine("正在统计字节数"); Stopwatch watch = new

线程系列05,手动结束线程

有时候,我们希望手动结束一个线程.比如说,当客户端满足某种条件,让线程就此结束.如何设计呢? 线程间肯定要共享数据,假设把共享数据默认值设置成true,客户端线程在满足某种条件时把共享数据设置成false.而另外一个线程,如果共享数据一直是true,它就一直执行程序,直到共享数据为false,该线程就结束. class Program { private static bool cancel = false; static void Main(string[] args) { Thread t

线程系列04,传递数据给线程,线程命名,线程异常处理,线程池

本篇体验:如何传递数据给线程,如何给线程命名,线程的异常处理,线程池.实在是太基础的部分. □ 传递数据给线程 ※ 使用Lambda表达式 class Program { static void Main(string[] args) { Thread t = new Thread(() => Say("hello", "world")); t.Start(); } static void Say(string msg, string msg1) { Cons

线程系列09,线程的等待、通知,以及手动控制线程数量

当一个线程直到收到另一个线程的通知才执行相关的动作,这时候,就可以考虑使用"事件等待句柄(Event Wait Handles)".使用"事件等待句柄"主要用到3个类: AutoResetEvent, ManualResetEvent以及CountdownEvent(.NET 4.0以后才有).本篇包括: ※ 一个线程等待另一个线程的通知※ 2个线程互相通知等待※ 一个线程等待队列中的多个任务通知※ 手动控制线程的数量 □ 一个线程等待另一个线程的通知 最简单的情景