当前位置: 首页 > article >正文

C# 多线程应用(同步异步)

线程:

我们日常生活中,打开电脑,我们可以一边听音乐,一边下载文件,一般浏览网页,三项工作可以同时进行.再比如一个网页里有一段Flash动画,还有一个等待用户输入的文本框,程序必须在播放动画的同时,不停的检测有无用户输入,以便及时响应用户的操作,在进程中创建两个线程来实现目标.一个线程检测用户输入,一个线程播放动画

概括:线程是操作系统资源调度的最小单位。线程不能独立运行,必须包含在进程中。进程中可以包含多个线程。多线程执行时是并行,无序的

进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

概括:进程是程序运行的环境。进程是线程的容器。

同步代码

同步代码是指代码按照书写的顺序执行,前一个操作没有完成前,不会进行下一个操作。

异步代码

异步代码是指代码在执行的时候,不会阻塞当前线程,而是在后台继续执行任务,当任务完成后,通过回调机制来通知调用者。


类型名称说明
属性Name线程的名称
CurrentThread获取当前正在运行的线程
Priority线程的优先级
ThreadState当前线程的状态
方法Start()开始执行线程
Suspend()挂起线程
Resume()恢复被挂起的线程
Sleep0将当前线程暂停一段时间
Interrupt()中断线程(即唤醒处于睡眠状态的线程
Join()待旧线程结束后执行新线程
Abort()中止线程
名 称说明
Unstarted线程尚未开始运行
Running线程正在正常运行
Suspended线程已经被挂起
SuspendRequested正在请求挂起线程,但还未来得及响应
WaitSleepJoin由于调用Wait()、Sleep()或 Join()方法而使线程处于阻塞状态
Stopped线程已经停止
StopRequested正在请求停止线程
AbortRequested已调用了Abort()方法,但还未收到threadAbortException异常
Aborted线程处于Stopped状态中
Background线程在后台执行

注意:分线程不能操作UI界面

因为UI界面是从主线程创建的,C#是不允许跨线程访问的

解决:

  1. CheckForIllegalCrossThreadCalls = false; 对非法的跨线程调用不进行检测(允许跨线程调用)
  2. 使用Invoke() 把耗时任务在分线程中执行,而页面更新放到主线程中

创建并使用线程

ThreadStart threadStart1 = new ThreadStart(ThradFun1);
  private void ThradFun1()
  {
      Console.WriteLine("分线程1");
  }

  Thread thread = new Thread(() =>
  {
      Console.WriteLine("分线程5");
  });
  thread.Start();//开启线程

Invoke调用

  //开辟一个分线程,把耗时任务放在分线程中,不在阻塞主线程
  Thread thread = new Thread(() =>
  {
      int result = Fac(40); 
     
      Invoke(new Action(() =>
      {
          label1.Text = result.ToString();
      }));

  });

  //启动线程
  thread.Start();
private int Fac(int x)
{
    if (x <= 1) return x;
    return Fac(x-1)+Fac(x-2);
}

线程的优先级及属性

    Thread t1 = new Thread(SX);
    Thread t2 = new Thread(SX);
    Thread t3 = new Thread(SX);
    t1.Name = "线程1";
    t2.Name = "线程2";
    t3.Name = "线程3";
//优先级
//  Highest> AboveNormal > Normal (正常)> BelowNormal>Lowest,
    t1.Priority=ThreadPriority.Normal;
    t2.Priority=ThreadPriority.AboveNormal;
    t3.Priority=ThreadPriority.Highest;
    t1.Start();
    t2.Start();
    t3.Start();

private void SX()
{
    Console.WriteLine($"线程的名称是{Thread.CurrentThread.Name},线程的Id是{Thread.CurrentThread.ManagedThreadId},线程的状态是{Thread.CurrentThread.ThreadState}");
}

示例

    Thread threadA = new Thread(() =>
    {
        for (int i = 0; i <= 1000; i++)
        {
            if (i % 10 == 0)
            {
                Console.Write('A');
            }
        }
    });
    Thread threadB = new Thread(() =>
    {
        for (int i = 0; i <= 1000; i++)
        {
            if (i % 10 == 0)
            {
                Console.Write('B');
            }
        }
    });
   
    //改变优先级
    threadA.Priority = ThreadPriority.AboveNormal;
    threadB.Priority = ThreadPriority.BelowNormal;
    threadA.Start();
    threadB.Start();

    //主线程执行代码
    for (int i = 0;i <= 1000;i++)
    {
        if (i % 10 == 0)
        {
            Console.Write('C');
        }
    }
   

执行多个线程

    CallbackMultOper(() =>
    {
        int sum = 0;
        for (int i = 0; i < 1000; i++)
        {
            sum += i;
        }
        Invoke(new Action(() =>
            {
                label1.Text = sum.ToString();
            }));
    }, () =>
    {
        int sum = 0;
        for (int i = 0; i < 10000; i++)
        {
            sum += i;
        }
        Invoke(new Action(() =>
        {
            label2.Text = sum.ToString();
        }));
    });  
}
private void CallbackMultOper(Action action1,Action action2)
{
    Thread t = new Thread(() =>
    {
        action1.Invoke();
        action2.Invoke();
    });
    t.Start();
    
}

线程数据槽

var slot1 = Thread.AllocateDataSlot();//匿名数据槽
LocalDataStoreSlot slot2 = Thread.AllocateNamedDataSlot("具名数据槽");
Thread.SetData(slot1, "张三");
Thread.SetData(slot2, "李四");
Thread tt = new Thread((s) =>
{
    Console.WriteLine($"分线程中读取的数据:\n 传递过来的{s},slot1中的{Thread.GetData(slot1)},slot2中的数据{Thread.GetData(slot2)}");
});
tt.Start("你好");

Console.WriteLine($"主线程中读取的数据{Thread.GetData(slot1)}");
Console.WriteLine($"主线程中读取的数据{Thread.GetData(slot2)}");

线程池

函数说明
SetMaxThreads()设置线程池中线程数目的上限,当任务超过上限时,多于的线程在线程池外排队 
SetMinThreads()设置线程池中线程数目的下限,当任务小于下限时,不足的用空线程补足
GetMaxThreads()获取线程池中线程数目的上限
GetMinThreads()获取线程池中线程数目的下限
GetAvailableThreads()查看还有多少线程可用,即最大线程数和当前活动线程数之间的差
QueueUserWorkItem()将任务排入线程池

 ThreadPool.QueueUserWorkItem((s) =>
 {
     for (int i = 0; i < 10; i++)
     {
         Console.WriteLine(s);
     }
 }, "kitty");
 ThreadPool.GetAvailableThreads(out int wokerThreads, out int asyncCount);
 Console.WriteLine($"{wokerThreads}+{ asyncCount}");

ThreadPoolText(); 

public static void ThreadPoolText()
 {
     for(int i = 0;i < 100;i++)
     {
         ThreadPool.QueueUserWorkItem(new WaitCallback(WorkItem), i);
     }
 }
 public static void WorkItem(object n)
 {
     Console.Write(n+"\t");
 }

Task 任务

//1
 Task t1 = new Task(() =>
 {
     Console.WriteLine("任务1");
 });
 Invoke(new WaitCallback((s) =>
 {
     label1.Text = s.ToString();
 }),"kitty");
t1.Start();

//2
  Task.Run(() =>
  {
      Console.WriteLine("任务2");
  });

  //3:
  //工厂模式  批量生产Task
  Task.Factory.StartNew(() =>
  {
      Console.WriteLine("任务3");
  });   

//4
 Task t3 = new Task(new Action<object>((s) =>
 {
     Console.WriteLine(s);
 }), "kitty");

//5
  Task t4 = new Task((s) =>
  {
      Console.WriteLine(s);
  }, "吴亦凡");

  t4.Start();

//简写
 new Task((s)=>
 {
     Console.WriteLine(s);
 },"吴亦凡").Start();

                  

异步操作(Asynchronously)

    Task t1 = Task.Run(() =>
    {
        for (int i = 0; i < 1000; i++)
        {
            Console.WriteLine("任务1");
        }
    });

    t1.Wait();//阻塞线程
    Task t2 = Task.Run(() =>
    {
        Console.WriteLine("任务2+=========");
    });

    t2.Wait();
    Task t3 = Task.Run(() =>
    {
        Console.WriteLine("任务3");
    });


//异步方法:按照异步执行(异步方法的业务逻辑在分线程中运行),不会阻塞主线程
//异步方法:在同步方法返回值前面添加一个关键字async,必须配合await关键字一起使用

private async void YB()
{
   Task task = new Task(() =>
{
    Console.WriteLine("异步");
});
task.Start();
//await 等待异步执行的结果
//await 后面等待的必须是一个任务 Task  Task<T>
await task;

await Task.Run(() =>
{
    Console.WriteLine("另外一个任务");
});
}

同步操作(Asychronously)

//主线程 
 Console.WriteLine("第一行代码,同步");
  for (int i = 0; i < 10000; i++)
  {
      Console.WriteLine("");
  }
  Console.WriteLine("第二行代码同步");


Task.Run(() =>
{
    Console.WriteLine("任务1");
});
Task.Factory.StartNew(() =>
{
    Console.WriteLine("任务2");
});    
Task t2 = new Task(() =>
{
    Thread.Sleep(1000);
    Console.WriteLine("任务3");
});
t2.start();
 Task t1 = new Task(() =>
 {
     Console.WriteLine("同步执行");
 });
//同步执行代码
t1.RunSynchronously()


SayHello3();

 private void SayHello1()
 {
     Console.WriteLine("SayHello1");
 }
 private void SayHello2()
 {
     Console.WriteLine("SayHello2");
     SayHello1();
 }
 private void SayHello3()
 {
     Console.WriteLine("SayHello3");
     SayHello2();
 }



WaitAny-WaitAll-WhenAny-ContinueWith

WaitAny()等待任务数组中任意一格任务完成,返回值是一个完成任务的索引值

WaitAll 等待所有的任务完成,没有返回值
WhenAny()等待任务数组中任意一个人物完成,返回的是Task<T>,可以通过链式调试执行其他的任务
ContinueWith() 继续执行其他的任务
  Task[] tasks = new Task[]
  {
      Task.Run(()=>
      {
          Thread.Sleep(1300);
          Console.WriteLine("线程1");
      }),
      Task.Run(()=>
      {
          Thread.Sleep(1200);
          Console.WriteLine("线程3");
      }),
      Task.Run(()=>
      {
          Thread.Sleep(1000);
          Console.WriteLine("线程3");
      }),
  };
  int result = Task.WaitAny(tasks);
  Console.WriteLine(result);//返回索引
  Task.WhenAny(tasks).ContinueWith(t =>
  {
      Console.WriteLine("完成一个");
  }).ContinueWith(t =>
  {
      Console.WriteLine("完成第二个");
  });




  Task.WhenAll(tasks).ContinueWith(t =>
  {
      Console.WriteLine("人物全完成了");
  });

Lock线程锁

目的:增强资源的安全性

作用: 一格资源锁住之后,只能被某个线程使用,其他的线程等待                                                

 下面来写一个案例

Monitor(管程)

Monitor类的部分方法
函数说明
Enter()获取临界资源的独占锁,若不成功,则睡眠在临界资源上
TryEnter()试图获取临界资源的独占锁,若不成功,立即返回
Pulse()唤醒睡眠在临界资源上的线程
PulseAll()唤醒睡眠在临界资源上的所有线程
Wait()释放独占锁并让当前线程睡眠在临界资源上
Exit()释放独占锁,退出临界区
 private static char proem;
 private static object obj=new object();// 创建一个锁 互斥锁

string s = "\t\t\t\t\t《浣溪沙》\n\t\t\t\t\t一曲新词酒一杯,\n\t\t\t\t\t去年天气旧亭台。\n\t\t\t\t\t夕阳西下几时回?\n\t\t\t\t\t无可奈何花落去,\n\t\t\t\t\t似曾相识燕归来。\n\t\t\t\t\t小园香径独徘徊。";
 Console.ForegroundColor = ConsoleColor.Red;
 Thread writer = new Thread(()=>
 {
    
     for (int i = 0; i <s.Length; i++)
     {
         lock (obj)
         {
                 proem = s[i];
                 Thread.Sleep(50);
                 Monitor.Pulse(obj);
                 Monitor.Wait(obj);
         }
     }
 });
 Console.ForegroundColor= ConsoleColor.Green;
 Thread reader = new Thread(()=>
 {
     for (int i = 0; i <s.Length; i++)
     {
         lock (obj)
         {
             char c = proem;
             Console.Write(c);
             //Thread.Sleep(20);
             Monitor.Pulse(obj);
             Monitor.Wait(obj);
         }
     }
 });
 writer.Priority = ThreadPriority.BelowNormal;
 reader.Priority = ThreadPriority.Lowest;
 writer.Start();
 reader.Start();

结果


http://www.kler.cn/a/539144.html

相关文章:

  • 使用Redis实现业务信息缓存(缓存详解,缓存更新策略,缓存三大问题)
  • DeepSeek 实践总结
  • windows生成SSL的PFX格式证书
  • 【大模型】DeepSeek-V3技术报告总结
  • jemalloc的malloc案例来分析GOT表和PLT表有关流程
  • 链表(LinkedList) 1
  • 《 C++ 点滴漫谈: 二十六 》控制流艺术:如何在 C++ 中驾驭程序逻辑
  • 深入讲解MyBatis
  • 启用MFA增强安全性防范勒索病毒
  • [AI]Mac本地部署Deepseek R1模型 — — 保姆级教程
  • TypeScript语言的多线程编程
  • 国产编辑器EverEdit - Web预览功能
  • DeepSeek繁忙时的最佳平替:阿里通义千问Qwen2.5-Max
  • 堆详解及C语言实现
  • (1/100)每日小游戏平台系列
  • React中使用​​useReducer​​​高阶钩子来管理状态
  • <论文>DeepSeek-R1:通过强化学习激励大语言模型的推理能力(深度思考)
  • Julia语言的安全开发
  • 德温特专利数据库字段说明
  • Leetcode面试经典150题刷题记录 —— 二分查找篇
  • 尝试一下,交互式的三维计算python库,py3d
  • MYSQL innodb引擎的索引结构,B+树一般都多高,层高怎么计算的?
  • BMS应用软件开发 — 12 菊花链通讯
  • day50 第十一章:图论part01
  • 本地大模型编程实战(11)与外部工具交互(2)
  • Java实现状态模式