首页 > 编程语言 >C#线程详解及应用示例

C#线程详解及应用示例

时间:2024-10-27 12:52:15浏览次数:8  
标签:string 示例 C# System 线程 using new public

 简介

在编写应用程序实现业务功能过程中,为解决吞吐量和响应效率的问题,我们会用到多线程、异步编程两项重要的技术。通过它们来提高应用程序响应和高效。应用程序每次运行都会启动一个进程(进程是一种正在执行的程序),而进程中可以包含一个或多个线程,由应用程序入口直接或间接执行的命令都由默认线程(或主线程)执行。

线程概述

线程是任务调度和执行的基本单位。线程是进程的一部分,线程共享该进程的资源。使用多线程技术可解决部分代码同时执行的需求,更好地利用资源。在介绍 C# 线程相关技术点前,先梳理同步、异步、线程安全等几个概念。

1、同步是指多个线程严格按照顺序依次执行,当前线程执行完成后再执行下一个线程。

2、异步是指多个线程的执行顺序是不确定的,每个线程都独立执行,互不干扰。

3、线程安全是指多线程访问共享资源时,保证数据的一致性和完整性,避免出现数据竞争和不一致的结果。

4、争用条件是指多个线程共享访问同一数据时,每个线程都尝试操作该数据,从而导致数据被破坏。

实现方式

在C#语言中,可以通过 Thread 、ThreadPool、Task、Parallel 等类来实现多线程,根据具体特点和场景选择合适的方式来实现。

使用 Thread

通过Thread类实例化 Thread 对象是在构造方法中传入委托对象,Thread 类的构造方法的参数 ThreadStart(无参无返回值的委托)和 ParameterizedThreadStart(有一个object类型参数但无返回值的委托)两种。

通过示例来了解实现无参数的线程


using System;
using System.Threading;
using System.Windows.Forms;

namespace Fountain.WinForm.ThreadApp
{
  public partial class ThreadForm : Form
  {
    public ThreadForm()
    {
      InitializeComponent();
    }
    /// <summary>
    /// 显示时间
    /// </summary>
    public void DisplayTime()
    {
      try 
      {
        while (true)
        {
          this.LabelCurrentTime.Invoke(new EventHandler(delegate
          {
            // 访问主界面的控件
            this.LabelCurrentTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
          }));
          Thread.Sleep(10);
        }
      }
      catch 
      {
      }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ThreadForm_Load(object sender, EventArgs e)
    {
      try
      {
        // 形式一
        Thread thread = new Thread(DisplayTime);
        // 设置为后台线程
        thread.IsBackground = true;
        // 开启线程
        thread.Start();
        // Lambda表达式代替方法DisplayTime
        Thread lambdaThread =new Thread(()=> 
        {
          this.LabelThreadId.Invoke(new EventHandler(delegate
          {
            // 访问主界面的控件
            this.LabelThreadId.Text = string.Format("Lambda代码段为执行的任务");
          }));
        });
         // 设置为后台线程
        lambdaThread.IsBackground = true;
        // 开启线程
        lambdaThread.Start();
      }
      catch
      {
      }
    }
  }
}

通过示例来了解实现带参数的线程

using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace Fountain.WinForm.ThreadApp
{
  public partial class ThreadForm : Form
  {
    public ThreadForm()
    {
      InitializeComponent();
    }
    /// <summary>
    /// 删除日志文件
    /// </summary>
    public void Delete(object retentionTime)
    {
      // 日志目录
      string logDirectory = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "Log");
      try
      {
        if (Directory.Exists(logDirectory))
        {
          // 获取所有匹配的日志文件
          string[] files = Directory.GetFiles(logDirectory, "*.log");
          // 遍历删除
          foreach (string file in files)
          {
            if (File.GetCreationTime(file).AddDays(Convert.ToInt32(retentionTime)) < DateTime.Now)
            {
              File.Delete(file);
            }
          }
        }
      }
      catch
      {
      }
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ThreadForm_Load(object sender, EventArgs e)
    {
      try
      {
        //声明线程实例
        Thread parameterThread = new Thread(Delete);
        // 设置为后台线程
        parameterThread.IsBackground = true;
        // 开始执行线程,传递参数
        parameterThread.Start(10); 
      }
      catch
      {
      }
    }
  }
}

其它常用方法

方法描述
Join()阻塞调用线程,直到某个线程终止或执行完。
Suspend()标记线程为挂起,进入暂停状态。
Resume()继续已经挂起的线程。
Interrupt()中断处于 WaitSleepJoin 状态的线程。
使用 ThreadPool

ThreadPool 是C# 提供的一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。

QueueUserWorkItem 方法

是用于将需要执行一个方法提交到线程池的队列。当线程池中有可用线程时,方法被执行。

通过示例解通过线程池的实现方式


using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace Fountain.WinForm.ThreadApp
{
  public partial class ThreadPoolForm : Form
  {
    public ThreadPoolForm()
    {
        InitializeComponent();
    }
    /// <summary>
    /// 窗体加载
    /// </summary>
    private void ThreadPoolForm_Load(object sender, EventArgs e)
    {
      try
      {
        // 最大线程数
        int workerThreads = 0;
        // 异步 I/O 最大线程数
        int completionPortThreads = 0;
        // 
        ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
        this.LabelMaxThreads.Text = string.Format("{0}{1}", this.LabelMaxThreads.Text, workerThreads);
        // 启动线程
        ThreadPool.QueueUserWorkItem(new WaitCallback(Delete),10);
      }
      catch 
      {
      }
    }
    /// <summary>
    /// 删除日志文件
    /// </summary>
    public void Delete(object retentionTime)
    {
      // 日志目录
      string logDirectory = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, "Log");
      try
      {
        if (Directory.Exists(logDirectory))
        {
          // 获取所有匹配的日志文件
          string[] files = Directory.GetFiles(logDirectory, "*.log");
          // 遍历删除
          foreach (string file in files)
          {
            if (File.GetCreationTime(file).AddDays(Convert.ToInt32(retentionTime)) < DateTime.Now)
            {
              File.Delete(file);
            }
          }
        }
        this.LabelCompleted.Invoke(new EventHandler(delegate
        {
            // 访问主界面的控件
            this.LabelCompleted.Text = "线程执行完成";
        }));
      }
      catch
      {
      }
    }
  }
}

RegisterWaitForSingleObject 方法

是将指定的操作方法注册到线程池,在接收到事件处理器信号、指定等待的时间超时,辅助线程执行此操作的方法。

参数描述
waitObject注册等待 WaitHandle 的委托。用ManualResetEvent 或 AutoResetEvent
callback在接收到事件处理器信号、指定等待的时间超时要执行的回调方法。
state传递给回调方法的参数
millisecondsTimeOutInterval等待的时间间隔,以毫秒为单位。0:立即返回,-1:无限等待。
executeOnlyOnce指示回调是否只执行一次。

示例:每隔1秒刷新界面时间


using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace Fountain.WinForm.ThreadApp
{
  public partial class ThreadPoolForm : Form
  {
    //创建一个AutoResetEvent,初始状态为未触发状态
    private AutoResetEvent autoResetEvent = new AutoResetEvent(false);
    /// <summary>
    /// 
    /// </summary>
    public ThreadPoolForm()
    {
      InitializeComponent();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ThreadPoolForm_Load(object sender, EventArgs e)
    {
      try
      {
        object state = new object();
        // 每隔1秒刷新界面时间
        ThreadPool.RegisterWaitForSingleObject(autoResetEvent,new WaitOrTimerCallback(TimerCallback), state, 1000,false);
      }
      catch 
      {
      }
    }
    /// <summary>
    /// 界面时间
    /// </summary>      
    private  void TimerCallback(object state, bool timedOut)
    {
      // 每隔1秒显示
      this.LabelCurrentTime.Invoke(new EventHandler(delegate
      {
          // 访问主界面的控件
          this.LabelCurrentTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
      }));
    }
  }
}

关于ManualResetEvent 和 AutoResetEvent 的用法,后续展开。

使用 Task

Task 是C# (.NET 4.0)提供的一种简单和强大的异步处理类。除Dispose()之外所有成员都是线程安全的,并且可以同时从多个线程使用。

创建任务

1、通过构造函数

// 创建
Task task = new Task(()=>
{
// 界面显示时间
this.LabelCurrentTime.Invoke(new EventHandler(delegate
  {
// 访问主界面的控件
this.LabelCurrentTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  }));
});
// 启动
task.Start();
// 定义一个任务
Action<object> action = (object retentionTime) =>
{
  Delete(retentionTime);
};
// 创建
Task deleteTask = new Task(action, 10);
// 启动
deleteTask.Start();

2、使用 TaskFactory.StartNew 方法

// 创建
Task taskFactory = Task.Factory.StartNew(() => {
  // 界面显示时间
  this.LabelCurrentTime.Invoke(new EventHandler(delegate
  {
    // 访问主界面的控件
    this.LabelCurrentTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  }));
});
启动
taskFactory.Start();

3、使用 Run 方法 .NET 4.5之后的版本

// 创建并启动
Task.Run(() => 
{
  // 显示时间
  this.LabelCurrentTime.Invoke(new EventHandler(delegate
  {
    // 访问主界面的控件
    this.LabelCurrentTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  }));
});
使用 Parallel

Parallel 是 C# (.NET 4.0) 提供的对并行循环和区域的支持。其提供三个静态方法作为结构化并行的基本形式。

Invoke:并行调用多个任务

/// <summary>
/// 显示时间
/// </summary>
public void DisplayTime()
{
}
/// <summary>
/// 删除日志文件
/// </summary>
private void Delete()
{
}
/// <summary>
/// 按钮事件
/// </summary>
private void ButtonTaskk_Click(object sender, EventArgs e)
{
    Parallel.Invoke(DisplayTime, Delete);
}

For:循环执行并行方法

Parallel.For(0, 20, i =>
{
  this.TextBoxResult.BeginInvoke(new EventHandler(delegate
  {
    // 访问主界面的控件
    this.TextBoxResult.Text += string.Format("执行次数:{0},开始时间:{1}{2}", i, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Environment.NewLine);
  }));
});

执行并行方法

List<string> weekend = new List<string>();
weekend.Add("星期日");
weekend.Add("星期一");
weekend.Add("星期二");
weekend.Add("星期三");
weekend.Add("星期四");
weekend.Add("星期五");
weekend.Add("星期六");

Parallel.ForEach(weekend,day =>
{
  this.TextBoxResult.BeginInvoke(new EventHandler(delegate
  {
    // 访问主界面的控件
    this.TextBoxResult.Text += string.Format("{0}{1}", day, Environment.NewLine);
  }));
});

小结

以上都是线程相关的内容,线程属于C#的高级语法,其内容有很多,使用情况也很多。希望通过本篇对大家对线程后续的学习有帮助,敬请关注后续内容。

标签:string,示例,C#,System,线程,using,new,public
From: https://blog.csdn.net/Funniyuan/article/details/143166032

相关文章

  • Chromium127调试指南 Windows篇 - 安装C++扩展与配置(五)
    前言在前面的文章中,我们已经安装了VisualStudioCode(VSCode)并配置了基本的扩展。现在,我们将进一步优化我们的开发环境,重点关注C++相关的依赖扩展。这些扩展对于在VSCode中高效开发和调试Chromium项目至关重要。此外,我们还将学习如何使用gn工具生成VisualStudio2022的项目......
  • CPU如何通过内存地址访问内存:寻址方式
    寻址方式:物理寻址分段寻址虚拟寻址分页寻址:引申出多级页表起源:寻址方式的发展取决于CPU位数和内存大小,16位就用物理分段寻址,32位用虚拟分段寻址或者2级分页寻址,64位一定用4级分页寻址了CPU的位数决定了:寻址能力:能够直接寻找地址的范围,比如16位的cpu只能找到从0......
  • 本地缓存库分析(二):bigcache
    文章目录本系列前言整体设计处理并发访问减少GC开销读写流程缓存淘汰性能优化用varint编码复用buffer栈上计算hash值数据结构GetSetDelete过期总结本系列本地缓存库分析(一):golang-lru本地缓存库分析(二):bigcache(本文)本地缓存库分析(三):freecache(未完待续)本地缓存库分析......
  • CSP-S2024 游记
    10.2至10.6训练。一堆whk作业根本没做。10.8晚自习突然接到10.9到10.25去训练的通知,瞬间不想写作业了。10.1110minT12h30minT230minT3,神奇!10.18听自己讲题的录像,发现自己南普很严重。10.19学校自己整的模拟赛。T1典题,T2简单dp,T3CF1408E,T4二位偏序板子......
  • Webpack和打包过程
    node中的内置模块pathpath常见的API在webpack中的使用认识webpack脚手架依赖webpackWebpack到底是什么呢?Webpack的安装Webpack的默认打包创建局部的webpackWebpack配置文件指定配置文件......
  • heic2any库使用教程
    heic2any是一个用于将HEIC/HEIF格式的图片转换为其他格式(如JPEG或PNG)的JavaScript库。这种格式通常由苹果设备(如iPhone和iPad)使用,因为它们在拍摄照片时默认保存为HEIC格式。如果你需要处理这些文件并将其转换成更通用的格式,heic2any就是一个很好的工具。以下是如......
  • CSP-S 2024 废物记
    CSP-S2024废物记省流版:10min打完T1,然后......然后?然后就没有然后了。我CSP是怎么打到这种分的?!怎么跟我上一次CSP一样废物......离退役也不远了,这样下去唯一能说的一句话就是“我们都有美好的未来”了......DAY-?初赛63.5pts,也就那样,虽然本来也就指望能过就行。......
  • C# 数字操作 (4)
    加减乘除inta=10;intb=20;Console.WriteLine($"a+b={a+b}");Console.WriteLine($"a-b={a-b}");Console.WriteLine($"a*b={a*b}");Console.WriteLine($"b/a={b/a}");a+b=30a-b=-10a*b=200b/a=2整数相除后自动去除小数d=a/......
  • #渗透测试#SRC漏洞挖掘# 信息收集-Shodan进阶VNC
    免责声明本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章阅读。                            ......
  • CSP-S 2024 游记
    Day1上午在渝北的机房里面玩,中午吃了饭去沙坪坝。在阶梯教室里面听同学们讲-J的事情,结果告诉我他们T4竟然是优化建图?题目还有14页,瞬间对下午充满了担忧。等了一会儿进考场了,听见机房里面好多同学考前就一直在敲键盘,不知道他们在写些什么。想了一会儿过后,把树剖和组合数......