【C#线程初级】Thread

1、命名空间与继承

命名空间:System.Threading
继承:Object→CriticalFinalizerObject→Thread

2、知识点

2.1 Thread.Sleep(0);

CPU竞争有很多种策略。Unix系统使用的是时间片算法,而Windows则属于抢占式的。如果一个线程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU
Windows中CPU根据优先级分配。Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。

2.2 线程生命周期:

线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。
下面列出了线程生命周期中的各种状态:

  • 未启动状态:当线程实例被创建但 Start 方法未被调用时的状况。
  • 就绪状态:当线程准备好运行并等待 CPU 周期时的状况。
  • 不可运行状态:下面的几种情况下线程是不可运行的:
    • 已经调用 Sleep 方法
    • 已经调用 Wait 方法
    • 通过 I/O 操作阻塞
  • 死亡状态:当线程已完成执行或已中止时的状况。

2.3 前台线程和后台线程

  • 前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程都是前台线程
  • 后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。

注意:后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。

2.4 跨线程访问

在这里插入图片描述
产生错误的原因:textBox1是由主线程创建的,thread线程是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。

解决方案:
1、在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。

private void Form1_Load(object sender, EventArgs e)
 {
        //取消跨线程的访问
        Control.CheckForIllegalCrossThreadCalls = false;
 }

2、使用回调函数Invoke
如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。
3、使用组件Background

3、常用属性和方法

属性描述
CurrentContext获取线程正在其中执行的当前上下文。
CurrentThread获取当前正在运行的线程。
ExecutionContext获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsAlive获取一个值,该值指示当前线程的执行状态。
IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId获取当前托管线程的唯一标识符。
Name获取或设置线程的名称。
Priority获取或设置一个值,该值指示线程的调度优先级。
ThreadState获取一个值,该值包含当前线程的状态。
方法描述
public void Abort()在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。
public static void Sleep( int millisecondsTimeout )让线程暂停一段时间。
public void Start()开始一个线程。
public static void ResetAbort()取消为当前线程请求的 Abort。
public void Join()在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式。
Resume()继续运行已挂起的线程。
Suspend()挂起当前线程,如果当前线程已属于挂起状态则此不起作用

Thread.Sleep() //当前线程sleep,在Main()就是主线程,在其他线程函数里就是当前分支线程sleep

4、类型

4.1 无参数委托类型

  • 委托定义:public delegate void ThreadStart();
    //声明委托类型
   //无参委托类型声明实例化
   ThreadStart start = new ThreadStart(ThreadProc);
   //Thread实例化
   Thread thread = new Thread(start);
   //线程启动
   thread .Start();
   
   //无参方法
   public static void ThreadProc() 
    {
      for (int i = 0; i <= 300; i++)
      {
         Console.WriteLine("这是无参的方法");
      }
    }
  • 除了可以运行静态的方法,还可以运行实例方法[类的方法]
class Program
    {
        static void Main(string[] args)
        {
            //创建ThreadTest类的一个实例
            ThreadTest test=new ThreadTest();
            //调用test实例的MyThread方法
            Thread thread = new Thread(new ThreadStart(test.MyThread));
            //启动线程
            thread.Start();
            Console.ReadKey();
        }
    }

    class ThreadTest
    {
        public void MyThread()
        {
            Console.WriteLine("这是一个实例方法");
        }
    }

如果为了简单,也可以通过匿名委托或Lambda表达式来为Thread的构造方法赋值

static void Main(string[] args)
 {
       //通过匿名委托创建
       Thread thread1 = new Thread(delegate() { Console.WriteLine("我是通过匿名委托创建的线程"); });
       thread1.Start();
       //通过Lambda表达式创建
       Thread thread2 = new Thread(() => Console.WriteLine("我是通过Lambda表达式创建的委托"));
       thread2.Start();
       Console.ReadKey();
 }

4.2 有参数委托类型

  • 委托定义:public delegate void ParameterizedThreadStart(Object obj)

注意:ParameterizedThreadStart委托的参数类型必须是Object的。如果使用的是不带参数的委托,不能使用带参数的Start方法运行线程,否则系统会抛出异常。但使用带参数的委托,可以使用thread.Start()来运行线程,这时所传递的参数值为null。

class Program
    {
        static void Main(string[] args)
        {
            //通过ParameterizedThreadStart创建线程
            Thread thread = new Thread(new ParameterizedThreadStart(Method));
            //给方法传值
            thread.Start("这是一个有参数的委托");
            Console.ReadKey();
        }
        /// <summary>
        /// 创建有参的方法
        /// 注意:方法里面的参数类型必须是Object类型
        /// </summary>
        /// <param name="obj"></param>
        static void Method(object obj)
        {
            Console.WriteLine(obj);
        }
    }

5、线程同步

Mutex 跟 lock 相似,但是 Mutex 支持多个进程。Mutex 大约比 lock 慢 20 倍

5.1 Lock关键字

5.2 Mutex互斥锁

  • Mutex 中文为互斥,Mutex 类叫做互斥锁。它还可用于进程间同步的同步基元
  • Windows 操作系统中,Mutex 同步对象有两个状态:
    • signaled:未被任何对象拥有;
    • nonsignaled:被一个线程拥有;

Mutex 只能在获得锁的线程中,释放锁

6、跨线程访问Invoke

6.1 定义

在拥有此控件的基础窗口句柄的线程上执行指定的委托。

Invoke方法会顺着控件树向上搜索,直到找到创建控件的那个线程(通常是主线程),然后进入那个线程改变控件的外观,确保不发生线程冲突。

Invoke重载
Invoke(Action)在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Invoke(Delegate)在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Invoke(Delegate, Object[])在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。
Invoke(Func)在拥有此控件的基础窗口句柄的线程上执行指定的委托。

偷懒可以使用属性设置为 来禁用 Control.CheckForIllegalCrossThreadCalls 异常 false。但是这样不安全。

6.2 属性 InvokeRequired

InvokeRequired 当前线程不是创建控件的线程时为true

6.3 用法

//不带参数
public delegate void MethodInvoker();
//带参数
public delegate void EventHandler(object sender, EventArgs e);
  • 标准写法
        private delegate void ModifyDelegate();
        private delegate void ModifyDelegatePar(string s, bool t);
        private void Invoke_Standard()
        {
            ModifyDelegate delge = new ModifyDelegate(Set);
            this.Invoke(delge);

            ModifyDelegatePar d2 = new ModifyDelegatePar(SetFirst);
            this.Invoke(d2, new object[] { "ni" , true });
        }
        private void Set()
        {
			//do something
        }

        private void SetFirst(string s, bool t)
        {
			//do something
        }
  • 常用写法1[Lamda表达式]
		//Action跟MethodInvoker差不多..都是无返回,无参数的委托
        private void Invoke_LamdaMethod()
        {
            this.Invoke(new Action(() =>
            {
                this.label1.Text = "桃花坞里桃花庵%".ToString();
                this.label2.Text = "桃花庵里桃花仙%".ToString();
                this.label3.Text = "桃花仙人种桃树%".ToString();
                this.label4.Text = "又摘桃花卖酒钱%".ToString();
            }));
        }
  • 常用写法2[匿名委托]

扩展 beginInvoke

MethodInvoker 提供一个简单的委托,用于使用 void 参数列表调用方法。 在调用控件 Invoke 的方法或需要简单委托但不想自行定义时,可以使用此委托。

扩展使用 BackgroundWorker多线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值