C# 多个线程调用一个方法并访问的同一个全局变量。模拟抢票软件

项目场景:

使用多线程访问全局变量,模拟抢票软件


实现过程:

1.num是一个全局任务,开启三个线程同时访问。模拟抢票 。 2.重点在于把总任务在子线程访问的时候先拿一个临时变量先取出来。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 总的数量
        /// </summary>
        public int num = 0;
        public int cycle = 0;
        public bool Threadflag = false;
        /// <summary>
        /// 定义一个委托类型
        /// </summary>
        public delegate void mydelegate1();
        /// <summary>
        /// 定义一个委托变量
        /// </summary>
        mydelegate1 MonitorHandle = null;

        private void Form1_Load(object sender, EventArgs e)
        {
            //绑定一个方法给monitorhandle(监控所有线程用)
            MonitorHandle = new mydelegate1(MonitorThread);
           
        }
        private object oj = new object();

        private void test1(object flag)
        {

            for (int j = 0; j <= cycle; j++)
            {
                int temp = 0;
                try
                {
                    //这里需要访问全局变量,加lock
                    lock (oj)
                    {
                        temp = num;
                        num--;
                    }

                    /* i = num*/
                    Thread.Sleep(500); ;
                    if (temp < 1)
                    {

                        Threadflag = true;
                        return;
                    }
                    else
                    {
                        //BeginInvoke异步方法向textbox里面添加log
                        this.textBox1.BeginInvoke(new Action(() =>
                        {
                            this.textBox1.AppendText("我是Thread:" + flag.ToString() + ">>>" + "剩余票数" + temp.ToString() + Environment.NewLine);
                        }));
                        //if (OnUpdataUI != null)
                        //    OnUpdataUI((int)flag, temp);

                    }

                }
                catch (Exception e)
                { }
            }

        }


        Thread td1 = null;
        Thread td2 = null;
        Thread td3 = null;
        private void button1_Click(object sender, EventArgs e)
        {
            Threadflag = false;
            num = 10;
            cycle = num;
            this.button1.Enabled = false;
            this.textBox1.Clear();
            Thread td = new Thread(ExcuThread);
            td.IsBackground = true;
            td.Start();
            //执行监控方法         
            if (MonitorHandle != null)
                MonitorHandle();

        }
        private void ExcuThread()
        {
            td1 = new Thread(test1);
            td2 = new Thread(test1);
            td3 = new Thread(test1);
            td1.Name = "Thread 1";
            td2.Name = "Thread 2";
            td3.Name = "Thread 3";
            td1.IsBackground = true;
            td2.IsBackground = true;
            td3.IsBackground = true;
            td1.Start(1);
            td2.Start(2);
            td3.Start(3);

        }
        /// <summary>
        /// 监控线程是否结束
        /// </summary>
        private void MonitorThread()
        {
            DateTime dt1 = DateTime.Now;
            while (true)
            {
                Application.DoEvents();
                //判断当前所有的线程是否停止
                if (Threadflag)
                {
                    Threadflag = false;
                    DateTime dt2 = DateTime.Now;
                    //计算总的行数和耗时
                    this.label1.Text = "总行数:" + this.textBox1.Lines.GetUpperBound(0).ToString() + " 耗时:" + ExecDateDiff(dt1, dt2) + "s";
                    this.button1.Enabled = true;                    
                    break;
                }
            }
        }
        /// <summary>
        /// 计算时间差值
        /// </summary>
        /// <param name="dateBegin">开始时间</param>
        /// <param name="dateEnd">结束时间</param>
        /// <returns></returns>
        private string ExecDateDiff(DateTime dateBegin, DateTime dateEnd)
        {
            TimeSpan ts1 = new TimeSpan(dateBegin.Ticks);
            TimeSpan ts2 = new TimeSpan(dateEnd.Ticks);
            TimeSpan ts3 = ts1.Subtract(ts2).Duration();            //你想转的格式
            return (ts3.TotalMilliseconds / 1000).ToString();
        }
    }
}



在这里插入图片描述

感兴趣的同学可以直接复制代码跑跑。

<think>嗯,用户这次问的是C#多线程同时调用同一个方法的问题,看来ta正在学习多线程编程。从引用内容看,用户之前应该接触过线程池、锁机制这些概念,但可能对具体实现细节还有困惑。用户提供的几个引用很有价值:引用[1]提到了Thread和Task这些基础工具;引用[2]展示了用lock保护共享列表的实例;引用[3]讨论了Mutex这种跨进程同步机制;引用[4]则展示了方法级同步属性。这些正好覆盖了线程安全的几个关键层面。用户核心想知道两点:能不能同时调用(技术上可以),以及如何安全地实现(重点)。ta可能遇到过发bug,或者正在设计一个多线程模块。考虑到C#多线程陷阱很多,需要特别注意几个层面:一是单纯方法调用本身(无状态方法完全安全),二是涉及共享状态时的保护(如静态变量),三是异常处理的特殊性。在准备回答时要突出三个维度:技术可行性(任何方法都能被调用)、安全性分级(分无状态/有状态讨论)、实战方案(代码示例必须包含lock/Mutex/原子操作等主流方案)。特别要强调lock的误用场景,比如字符串锁这种坑。最后生成的三个问题很有递进性:从基础原理到调试技巧再到架构选择,这样用户可以根据自己进度逐步深入。尤其“锁竞争”这个问题设计得好,很多初学者卡在这个点上。</think> 在C#中,**多个线程可以同时调用同一个方法**,但需要根据方法内部是否访问共享状态来采取不同的线程安全措施。以下是实现方式和注意事项: --- ### 一、实现方式(分场景讨论) #### 1. **无状态方法线程安全)** ```csharp public int Add(int a, int b) return a + b; // 无需同步(无共享状态) ``` **说明**:纯计算且不访问共享资源的方法可被任意多线程调用。 #### 2. **访问共享资源的方法(需同步)** ```csharp private List<int> _sharedList = new List<int>(); public void AddItem(int item) { lock (_sharedList) { // 使用lock保护共享集合 _sharedList.Add(item); } } ``` **关键工具**: - **`lock`关键字**:保护临界区(同一时刻仅允许一个线程进入)[^3] - **`Mutex`类**:跨进程同步(如控制单实例应用)[^3] - **`Semaphore`/`SemaphoreSlim`**:限制线程数 - **原子操作**:`Interlocked.Increment(ref counter)` --- ### 二、注意事项 1. **共享状态保护** - 静态变量、单例对象、全局集合等必须加锁。 - 错误示例(未加锁导致数据竞争): ```csharp // 线程不安全! public static void Append(string text) { _logBuffer += text; // 多线程同时修改字符串会丢失数据 } ``` 2. **锁粒度控制** - **过粗**:降低发性能(如锁整个方法)[^4] - **过细**:增加死锁风险 - 建议:仅锁访问共享资源的代码段。 3. **避免死锁** - 原则:按固定顺序获取多个锁,用`Monitor.TryEnter`设置超时。 - 危险场景: ```csharp lock (objA) { lock (objB) { ... } // 若其他线程反向锁定objB->objA则死锁 } ``` 4. **`async`方法中的同步** - **禁止用`lock`**:可能阻塞线程线程导致饥饿。 - **替代方案**:`SemaphoreSlim.WaitAsync()`、`ConcurrentQueue`等异步友好结构。 --- ### 三、最佳实践代码示例 ```csharp private static readonly object _locker = new object(); // 专用锁对象 private static int _counter = 0; public void ThreadSafeMethod() { // 非临界区代码(可行执行) var localData = LoadLocalData(); // 临界区:保护共享状态 lock (_locker) { _counter++; _sharedList.Add(localData); } } ``` > 📌 **关键原则**: > - 无共享状态 → 无需同步(理想情况) > - 有共享状态 → **必须通过锁/原子操作/线程安全集合(如`ConcurrentBag`)保护** ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值