C#线程的参数传递、获取线程返回值以及处理多线程冲突

news2024/11/27 4:19:00

  C#作为一门优秀的开发语言,现在国内的流行度貌似不如以前,大家都不在意它的无所不能了。

  C#的灵活与强大只有在经常使用中才会有所领悟,适当地掌握它还是有必要的。

  在这里总结一下线程的传递参数以及获取线程的返回值,还有处理多线程之间可能引发的冲突以及解决办法。

  在C#中,开启一个线程很容易。

Thread Th1= new Thread(func);
Th1.Start();

private void func(object Obj)
{
    //处理代码
}

  很多情况下,我们是需要对线程进行传递参数的,这个也简单。

  1、线程的单一参数传递

        private void button1_Click(object sender, EventArgs e)
        {
            Thread Th1= new Thread(func);
            Th1.Start("CSDN");
            Thread.Sleep(500);
        }

        private void func(object Obj)
        {
            string Str = Obj as string;
            textBox1.BeginInvoke(new Action(() =>
            {
                textBox1.Text = $"传入的参数:{Str}";
            }));
        }

  2、线程的多参数传递以及返回值

  上面的例子是单一的参数,参数要求是对象,使用的时候进行了拆箱,根据上面的例子对于多参数,可以使用中间对象来处理,就是在中间对象中放置参数和获取处理后的结果。

        private void button1_Click(object sender, EventArgs e)
        {
            FinancialInfo FI=new FinancialInfo();
            FI.PersonName = "CSDN";
            FI.PersonDeposit = 123;
            Thread Th1 = new Thread(FI.ThreadChangeFinanceialInfo);
            Th1.Start();
            Thread.Sleep(500);
            textBox1.Text=FI.PersonName+Environment.NewLine+FI.PersonDeposit.ToString();
        }

        private class FinancialInfo
        {
            private string Name=string.Empty;
            private int Deposit=0;
            public string PersonName
            {
                get { return Name; }
                set { Name = value; }
            }

            public int PersonDeposit
            {
                get { return Deposit; }
                set { Deposit = value; }
            }

            public void ThreadChangeFinanceialInfo() 
            {
                this.Name = this.Name + " | C#";
                this.Deposit = this.Deposit + 100;
            }
        }

  3、使用Task.Result获取任务返回值

  上面的方法看似比较繁琐,使用Task则比较简单。

            FinancialInfo fi=new FinancialInfo();
            fi.PersonName = "CSDN";
            fi.PersonDeposit = 123;

            var task1 = Task.Factory.StartNew( fi.ThreadChangeFinanceialInfoResult );

            fi = task1.Result;
            textBox1.Text = textBox1.Text + fi.PersonName +"====="+fi.PersonDeposit.ToString()+Environment.NewLine;

  FinancialInfo定义与上面一样。

        private class FinancialInfo
        {
            private string Name=string.Empty;
            private int Deposit=0;
            public string PersonName
            {
                get { return Name; }
                set { Name = value; }
            }

            public int PersonDeposit
            {
                get { return Deposit; }
                set { Deposit = value; }
            }

            public void ThreadChangeFinanceialInfo()
            {
                this.Name = this.Name + " | C#";
                this.Deposit = this.Deposit + 100;
            }

            public FinancialInfo ThreadChangeFinanceialInfoResult()
            {
                FinancialInfo fi=new FinancialInfo();
                fi.Name = this.Name + " | C#";
                fi.Deposit = this.Deposit + 100;
                return fi;
            }
        }

  上面的写法就好看多了。

  4、多线程可能引起的冲突以及解决办法

  多线程在处理同一对象时容易引起潜在的冲突,这个显而易见,例如:

        private void button1_Click(object sender, EventArgs e)
        {
            FinancialInfo FI = new FinancialInfo();
            FI.PersonName = "CSDN";
            FI.PersonDeposit = 123;
            Thread Th1 = new Thread(FI.ThreadAdd);
            Thread Th2 = new Thread(FI.ThreadReduce);
            Th1.Start();
            Th2.Start();
            Thread.Sleep(5000);
            textBox1.Text = textBox1.Text + FI.PersonName +"|"+FI.PersonDeposit.ToString()+Environment.NewLine;
        }

        private class FinancialInfo
        {
            private string Name=string.Empty;
            private int Deposit=0;
            public string PersonName
            {
                get { return Name; }
                set { Name = value; }
            }

            public int PersonDeposit
            {
                get { return Deposit; }
                set { Deposit = value; }
            }

            public void ThreadAdd()
            {
                for (int i = 0; i < 1000000; i++)
                {
                    this.Deposit = this.Deposit + 1;
                }
            }

            public void ThreadReduce()
            {
                for (int i = 0; i < 1000000; i++)
                {
                    this.Deposit = this.Deposit - 1;
                }
            }
        }

  显示结果:

  按道理, FI.PersonDeposit的值是123,加了1000000,也减了1000000,那么最终的结果应该还是123,为什么会是这样呢?

  这就是多线程在处理同一对象时所产生的冲突了,产生的就是所谓的“脏数据”。

  上面的代码因为等待线程执行完,进行了休眠,可以使用Task来写更简单。

            var task1 = new Task(FI.ThreadAdd);
            var task2 = new Task(FI.ThreadReduce);
            task1.Start();
            task2.Start();

            Task.WaitAll(task1,task2);

  Task是比Thread更加高级的概念,一个Task至少包含一个Thread。

  解决上面的冲突就是对可能引起冲突的对象进行加锁判断。

  完整代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Text;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MultiThread
{
    public partial class Form3 : Form
    {

        private static readonly object LockObj=new object();

        public Form3()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            FinancialInfo FI = new FinancialInfo();
            FI.PersonName = "CSDN";
            FI.PersonDeposit = 123;
            var task1 = new Task(FI.ThreadAdd);
            var task2 = new Task(FI.ThreadReduce);
            task1.Start();
            task2.Start();
            Task.WaitAll(task1, task2);
            textBox1.Text = textBox1.Text + FI.PersonName +"|"+FI.PersonDeposit.ToString()+Environment.NewLine;
        }


        private class FinancialInfo
        {
            private string Name=string.Empty;
            private int Deposit=0;
            public string PersonName
            {
                get { return Name; }
                set { Name = value; }
            }

            public int PersonDeposit
            {
                get { return Deposit; }
                set { Deposit = value; }
            }

            public void ThreadChangeFinanceialInfo() 
            {
                this.Name = this.Name + " | C#";
                this.Deposit = this.Deposit + 100;
            }

            public void ThreadAdd()
            {
                for (int i = 0; i < 1000000; i++)
                {
                    lock(LockObj)
                    {
                        this.Deposit = this.Deposit + 1;
                    }                    
                }
            }

            public void ThreadReduce()
            {
                for (int i = 0; i < 1000000; i++)
                {
                    lock(LockObj)
                    {
                        this.Deposit = this.Deposit - 1;
                    }                    
                }
            }
        }
    }
}

  显示结果:

  上面显示出了正确的结果,但是会耗时。

  5、多线程之间的协调

  在多线程需要独占访问资源的时候,AutoResetEvent 允许线程通过发信号互相通信。
  通过 WaitOne 来等待信号,调用 Set 发出资源可用的信号。
  使用也比较简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/47990.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【毕业设计】17-基于单片机的矿井提升机_步进电机控制装置设计(原理图+仿真+源代码+实物图+答辩论文+答辩PPT)

typora-root-url: ./ 【毕业设计】17-基于单片机的矿井提升机_步进电机控制装置设计&#xff08;原理图仿真源代码实物图答辩论文答辩PPT&#xff09; 文章目录typora-root-url: ./【毕业设计】17-基于单片机的矿井提升机_步进电机控制装置设计&#xff08;原理图仿真源代码实…

IB中文(语言与文学)介绍分析

Intro to Group 1 Subjects&#xff1a;语言A 语言A科目主要针对学生的母语语言运用与文学鉴赏能力进行考核&#xff0c;旨在培养学生文学素养的同时注重社会关怀、全球视角与批判性思维的意识形成。 IB文凭要求每位学生至少在语言A中学习一门课程。 今天我们就来仔细介绍一下其…

全波形反演的深度学习方法: 第三章 常规反演

本章介绍反演的基础知识, 以及工程中的常规反演. 仅供内部培训. 3.1 地震数据采集 地震勘探中常使用人工激发的振动进行数据采集. 相应装置包括: 激发器是产生震动的装置, 如炸药, 地震车 (撞击地面). 在城市道路等具有车辆会产生振动的地方, 也可以不安装这类装置;地震检波…

为什么我推荐用户故事地图?

最近在给几个客户的培训和辅导中&#xff0c;都用到了用户故事地图(User Story Mapping or USM)&#xff0c;越来越发现USM是一个强大的工具。USM有它特殊的妙用之处和适用范围&#xff0c;我们不用指望USM能解决多的问题&#xff0c;比如产品架构和用户体验旅程就不在USM的范围…

TSN新技术,让您的设备网络“更实时、更确定、更安全”

近日,工业和信息化部发布了2022年第23号公告,批准发布行业标准YD/T 4134-2022《工业互联网时间敏感网络需求及场景》。该标准是国内首个时间敏感网络(TSN)技术标准,标志着我国TSN技术标准体系建设迈出了坚实的一步,对构建工业互联网网络标准体系具有重要意义。 TSN源于Time…

【解决方法】错误:无法和SFTP服务器建立FTP连接,请选择合适的协议

在家连实验室的服务器时&#xff0c;出现了如下的情况&#xff1a;当时一直在想为啥协议不合适。 解决方案&#xff1a; 1. 点击【文件】->【站点管理器】&#xff0c;出现如下界面&#xff1a; 可能你的那里没有新站点&#xff0c;如果没有就点击新站点哦。 2. 点击【协议…

社区系统项目复盘-4

文章目录Redis高性能存储什么是Redis&#xff1f;为什么使用Redis&#xff1f;Spring是怎么整合Redis的&#xff1f;Redis事务管理点赞关注优化登录模块Redis高性能存储 使用redis实现了点赞、关注相关的功能&#xff0c;优化了登录模块。 什么是Redis&#xff1f; Redis是一…

[附源码]计算机毕业设计springboot基于vue的软件谷公共信息平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

力扣(LeetCode)895. 最大频率栈(C++)

设计 ①维护最大频率&#xff0c;②维护每个数的出现次数&#xff0c;③维护出现次数对应的栈。 压栈时&#xff0c;新数压入出现次数对应的栈&#xff0c;每次压入新数&#xff0c;维护最大频率(所有出现次数中的最大出现次数)。 弹栈时&#xff0c;找最大频率对应的栈&…

RealEvo许可证协议

一直使用RealEvo V4.0.0版&#xff0c;今天因license到期&#xff0c;索性一同安装下新版的IDE。 当前最新RealEvo IDE版本为 V5.0.5&#xff0c;V5.0.0~V5.0.3版本存在一些bug&#xff0c;建议更新。 SylixOS 许可信息如下&#xff08;开源版本&#xff09; SylixOS嵌入式实…

【毕业设计】前后端分离——实现登录注册功能

&#x1f308;据说&#xff0c;看我文章时 关注、点赞、收藏 的 帅哥美女们 心情都会不自觉的好起来。 前言&#xff1a; &#x1f9e1;作者简介&#xff1a;大家好我是 user_from_future &#xff0c;意思是 “ 来自未来的用户 ” &#xff0c;寓意着未来的自己一定很棒~ ✨个…

Word控件Spire.Doc 【图像形状】教程(8): 如何借助C#/VB.NET在 Word 中插入艺术字

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

基于51单片机的智能蓝牙路灯街灯控制系统proteus仿真原理图PCB

功能&#xff1a; 0.本系统采用STC89C52作为单片机 1.液晶实时显示时间/环境光强/工作模式 2.每隔5s向蓝牙串口发送一次信息 3.支持路灯故障检测 4.工作时间18~24时&#xff0c;两个路灯同时点亮&#xff0c;24时以后&#xff0c;B路灯关闭&#xff0c;若检测到由物体通过&…

【Kafka】Kafka的重复消费和消息丢失问题

文章目录前言一、重复消费1.1 重复消费出现的场景1.1.1 Consumer消费过程中&#xff0c;进程挂掉/异常退出1.1.2 消费者消费时间过长1.2 重复消费解决方案1.2.1 针对于消费端挂掉等原因造成的重复消费问题1.2.2 针对于Consumer消费时间过长带来的重复消费问题二、消息丢失2.1 生…

Multipass,多平台本地轻量级Linux体验!

Multipass 是由Ubuntu官方提供&#xff0c;在Linux&#xff0c;MacOS和Windows上快速生成 Ubuntu虚拟机 的工具。它提供了一个简单但功能强大的CLI&#xff0c;可让我们在本地快速进入Ubuntu系统环境并使用Linux命令&#xff0c;亦可以在本地计算机创建自己的迷你型云服务器。总…

zabbix监控网络连接状态

目录 一、环境准备 二、网络连接状态介绍 三、自定义监控key 四、给主机添加监控项 一、环境准备 1、搭建zabbix基础环境 zabbix基础环境部署参照&#xff1a;zabbix基础环境部署_桂安俊kylinOS的博客-CSDN博客 2、给web1安装nginx环境&#xff0c;并加载status模块 以…

SpringCloud学习笔记 - 链路监控 - SpringCloud Sleuth

1. Sleuth简介 在微服务框架中&#xff0c;一个由客户端发起的请求&#xff0c;在后端系统中会调用多个不同的的服务节点&#xff0c;来协同产生最后的响应结果&#xff0c;因此每一个前端请求都会形成一条复杂的分布式服务调用链路&#xff0c;链路中的任何一环出现高延时或错…

信号量Semaphore详解

前言 大家应该都用过synchronized 关键字加锁&#xff0c;用来保证某个时刻只允许一个线程运行。那么如果控制某个时刻允许指定数量的线程执行&#xff0c;有什么好的办法呢? 答案就是JUC提供的信号量Semaphore。 介绍和使用 Semaphore&#xff08;信号量&#xff09;可以用…

iwebsec靶场 SQL注入漏洞通关笔记10- 双重url编码绕过

系列文章目录 iwebsec靶场 SQL注入漏洞通关笔记1- 数字型注入_mooyuan的博客-CSDN博客 iwebsec靶场 SQL注入漏洞通关笔记2- 字符型注入&#xff08;宽字节注入&#xff09;_mooyuan的博客-CSDN博客 iwebsec靶场 SQL注入漏洞通关笔记3- bool注入&#xff08;布尔型盲注&#…

VF01销售开票发票金额控制增强

实施隐式增强 全部代码如下&#xff1a; method IF_EX_BADI_SD_BILLING~INVOICE_DOCUMENT_CHECK. CALL FUNCTION ‘SIPT_DOC_CHECK_SD’ EXPORTING it_xvbrk fxvbrk it_xvbrp fxvbrp it_xkomv fxkomv it_xvbpa fxvbpa IMPORTING ev_bad_data fbad_data. “”“”“”“…