吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1666|回复: 18
收起左侧

[求助] C# 多线程疑惑?

[复制链接]
Cool_Breeze 发表于 2021-3-20 18:01
25吾爱币
本帖最后由 Cool_Breeze 于 2021-3-20 18:03 编辑

源代码:
[C#] 纯文本查看 复制代码
using System;
using System.Threading;
using System.Collections;
using System.Collections.Concurrent;

class Multimedia
{
    static Args[] argsArr = 
    {
        new Args(),
        new Args(),
        new Args(),
        new Args(),
    };
    
    public static void Main()
    {
        // 创建线程池
        Thread[] th = new Thread[4];
        for (int i=0; i<th.Length; i++)
        {
            th[i] = new Thread(Multimedia.getNumber);
            th[i].Name = i.ToString();
            th[i].Start(argsArr[i]);
        }
        
        // 向队列发送信息
        for (int i=0; i<20; i++)
        {
            Args.queue.Enqueue(i);
        }

        // 等待线程结束
        int flag = 0;
        while (true)
        {
            foreach (var n in th)
            {
                if (n.ThreadState == ThreadState.Stopped) flag++;
                else
                {
                    flag = 0;break;
                }
            }
            if (flag == th.Length) break;
        }
        
        // Thread.Sleep(2000);
        foreach (var n in argsArr)
            Console.WriteLine(n.GetCount()); // 最后结果 40 正确
        Console.ReadKey();
    }
    
    class Args
    {
        public static readonly ConcurrentQueue<int> queue = new ConcurrentQueue<int>(); // 只会创建一次
        private static object Lock = new object();
        private static int Count = 0;
        
        // private static readonly int[] extra = {1,2,3,4,5,6,7,8,9,10};
        public static readonly object extraLock = new object();
        // private static int extraIdex = 0;        
        private readonly int[] extra = {1,2,3,4,5}; // 主线程信息发送完成,在发送这个信息
        private int extraIdex = 0;
        
        public void Add()
        {
            lock(Lock)
            {
                Count++;
                Console.WriteLine("++++++++++++++++++++++++++++++++++++{0}",Count);
            }
        }
        public int GetCount()
        {
            return Count;
        }
        
        // public int GetExtraAdd()
        // {
            // lock(extraLock) // 访问已经锁了
            // {
                // if (extraIdex >= extra.Length) return -1; // 使线程结束
                // else return extra[extraIdex++];
            // }
        // }
        public int GetExtraAdd() // 返回 extra 一个元素
        {
            int temp = 0;
            lock (extraLock)
            {
                if (extraIdex >= extra.Length) return -1; // 使线程结束
                temp = extra[extraIdex];
                extraIdex++;
                return temp;
            }
        }
    }
    
    static void getNumber(object argsEx)
    {
        Args args = argsEx as Args;
        int temp = 0;            
        while (true)
        {
            // 调用这条语句,最后结果就会受影响
            // Console.WriteLine("当前线程名:{0} 当前 Count:{1}", Thread.CurrentThread.Name, temp);
            try
            {
                Monitor.Enter(Args.queue);
                lock(Args.queue) // 只有保证这四个线程锁的是同一个引用对象就行
                {
                    if (Args.queue.TryDequeue(out temp) == false)
                    {
                        Args.queue.Enqueue(args.GetExtraAdd()); // 额外添加一点数据
                        continue;
                    }
                    if (temp == -1) break; // 线程结束
                }
                args.Add(); // 成功获取值以后Args.Count ++;
            }
            // catch
            // {
                // lock(Args.extraLock)
                // {                    
                    // Args.queue.Enqueue(args.GetExtraAdd()); // 额外添加一点数据
                // }
            // }
            finally
            {
                Monitor.Exit(Args.queue);
            }
        }
        Console.WriteLine("{0} 已经退出", Thread.CurrentThread.Name);
    }
}


输出正确结果为:
++++++++++++++++++++++++++++++++++++1
++++++++++++++++++++++++++++++++++++2
++++++++++++++++++++++++++++++++++++3
++++++++++++++++++++++++++++++++++++4
++++++++++++++++++++++++++++++++++++5
0 已经退出
++++++++++++++++++++++++++++++++++++6
++++++++++++++++++++++++++++++++++++7
++++++++++++++++++++++++++++++++++++8
++++++++++++++++++++++++++++++++++++9
++++++++++++++++++++++++++++++++++++10
1 已经退出
++++++++++++++++++++++++++++++++++++11
++++++++++++++++++++++++++++++++++++12
++++++++++++++++++++++++++++++++++++13
++++++++++++++++++++++++++++++++++++14
++++++++++++++++++++++++++++++++++++15
2 已经退出
++++++++++++++++++++++++++++++++++++16
++++++++++++++++++++++++++++++++++++17
++++++++++++++++++++++++++++++++++++18
++++++++++++++++++++++++++++++++++++19
++++++++++++++++++++++++++++++++++++20
++++++++++++++++++++++++++++++++++++21
++++++++++++++++++++++++++++++++++++22
++++++++++++++++++++++++++++++++++++23
++++++++++++++++++++++++++++++++++++24
++++++++++++++++++++++++++++++++++++25
++++++++++++++++++++++++++++++++++++26
++++++++++++++++++++++++++++++++++++27
++++++++++++++++++++++++++++++++++++28
++++++++++++++++++++++++++++++++++++29
++++++++++++++++++++++++++++++++++++30
++++++++++++++++++++++++++++++++++++31
++++++++++++++++++++++++++++++++++++32
++++++++++++++++++++++++++++++++++++33
++++++++++++++++++++++++++++++++++++34
++++++++++++++++++++++++++++++++++++35
++++++++++++++++++++++++++++++++++++36
++++++++++++++++++++++++++++++++++++37
++++++++++++++++++++++++++++++++++++38
++++++++++++++++++++++++++++++++++++39
++++++++++++++++++++++++++++++++++++40
3 已经退出
40
40
40
40
调用 107行 这一行语句就会出现结果不对!!!
// 调用这条语句,最后结果就会受影响
            // Console.WriteLine("当前线程名:{0} 当前 Count:{1}", Thread.CurrentThread.Name, temp);
错误结果小于 40 (这个结果会变动,不固定,有时候又是正确的)
错误输出:
当前线程名:0 当前 Count:0
当前线程名:1 当前 Count:0
当前线程名:0 当前 Count:0
++++++++++++++++++++++++++++++++++++1
当前线程名:1 当前 Count:1
++++++++++++++++++++++++++++++++++++2
当前线程名:1 当前 Count:2
当前线程名:1 当前 Count:0
++++++++++++++++++++++++++++++++++++3
当前线程名:1 当前 Count:1
++++++++++++++++++++++++++++++++++++4
当前线程名:1 当前 Count:0
++++++++++++++++++++++++++++++++++++5
当前线程名:1 当前 Count:1
++++++++++++++++++++++++++++++++++++6
当前线程名:1 当前 Count:2
++++++++++++++++++++++++++++++++++++7
当前线程名:1 当前 Count:3
++++++++++++++++++++++++++++++++++++8
当前线程名:1 当前 Count:4
++++++++++++++++++++++++++++++++++++9
当前线程名:1 当前 Count:5
++++++++++++++++++++++++++++++++++++10
当前线程名:1 当前 Count:6
++++++++++++++++++++++++++++++++++++11
当前线程名:1 当前 Count:7
++++++++++++++++++++++++++++++++++++12
当前线程名:1 当前 Count:8
++++++++++++++++++++++++++++++++++++13
当前线程名:1 当前 Count:9
++++++++++++++++++++++++++++++++++++14
当前线程名:1 当前 Count:10
++++++++++++++++++++++++++++++++++++15
当前线程名:1 当前 Count:11
++++++++++++++++++++++++++++++++++++16
当前线程名:1 当前 Count:12
++++++++++++++++++++++++++++++++++++17
当前线程名:1 当前 Count:13
++++++++++++++++++++++++++++++++++++18
当前线程名:1 当前 Count:14
++++++++++++++++++++++++++++++++++++19
当前线程名:0 当前 Count:0
当前线程名:3 当前 Count:0
当前线程名:1 当前 Count:15
当前线程名:2 当前 Count:0
++++++++++++++++++++++++++++++++++++20
当前线程名:0 当前 Count:16
++++++++++++++++++++++++++++++++++++21
当前线程名:3 当前 Count:17
++++++++++++++++++++++++++++++++++++22
当前线程名:1 当前 Count:18
++++++++++++++++++++++++++++++++++++23
当前线程名:2 当前 Count:19
++++++++++++++++++++++++++++++++++++24
当前线程名:2 当前 Count:3
++++++++++++++++++++++++++++++++++++25
当前线程名:2 当前 Count:1
当前线程名:2 当前 Count:0
++++++++++++++++++++++++++++++++++++26
当前线程名:2 当前 Count:1
当前线程名:2 当前 Count:0
++++++++++++++++++++++++++++++++++++27
当前线程名:2 当前 Count:2
当前线程名:2 当前 Count:0
当前线程名:0 当前 Count:0
当前线程名:3 当前 Count:0
++++++++++++++++++++++++++++++++++++28
当前线程名:2 当前 Count:3
当前线程名:2 当前 Count:0
++++++++++++++++++++++++++++++++++++29
当前线程名:2 当前 Count:4
++++++++++++++++++++++++++++++++++++30
当前线程名:2 当前 Count:4
当前线程名:2 当前 Count:0
++++++++++++++++++++++++++++++++++++31
当前线程名:2 当前 Count:5
当前线程名:2 当前 Count:0
当前线程名:0 当前 Count:0
当前线程名:1 当前 Count:0
3 已经退出
++++++++++++++++++++++++++++++++++++32
当前线程名:2 当前 Count:2
当前线程名:1 当前 Count:0
++++++++++++++++++++++++++++++++++++33
当前线程名:2 当前 Count:3
当前线程名:2 当前 Count:0
2 已经退出
当前线程名:0 当前 Count:0
++++++++++++++++++++++++++++++++++++34
当前线程名:1 当前 Count:5
1 已经退出
当前线程名:0 当前 Count:0
当前线程名:0 当前 Count:0
0 已经退出
34
34
34
34

最佳答案

查看完整内容

你想要正确结果,好像是同步的吧,你代码逻辑有问题

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
hcl1986 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| Cool_Breeze 发表于 2021-3-20 21:02
WolfAvenue 发表于 2021-3-20 20:03
你想要正确结果,好像是同步的吧,你代码逻辑有问题

替换 第 93 行 的 return -1 为 Thread.CurrentThread.Abort() 完美解决!
感谢!
WolfAvenue 发表于 2021-3-20 18:01
Cool_Breeze 发表于 2021-3-20 19:04
对啊! System.Collections.Queue,不是线程安全的!网上搜索查到 System.Collections.Concurrent 是线程 ...

你想要正确结果,好像是同步的吧,你代码逻辑有问题
WolfAvenue 发表于 2021-3-20 18:55
 楼主| Cool_Breeze 发表于 2021-3-20 19:04
WolfAvenue 发表于 2021-3-20 18:55
脑阔痛,好复杂的样子,并发取队列?

对啊! System.Collections.Queue,不是线程安全的!网上搜索查到 System.Collections.Concurrent 是线程安全的!
还是出现问题了!实在搞不明白,到底是哪里出了问题!
ykrne 发表于 2021-3-20 19:41
尽量不要用 Thread, 用 Task
 楼主| Cool_Breeze 发表于 2021-3-20 20:36
WolfAvenue 发表于 2021-3-20 20:03
你想要正确结果,好像是同步的吧,你代码逻辑有问题

恩,我也有感觉,好像是114 更 118 这几行!
Args.queue.TryDequeue(out temp) == false 如果成功在 ConcurrentQueue<T> 开头处移除并返回了元素,则为 true;否则为 false。
其实就是主线程有一个20数量的列表,加 (4个)子线程里面每个线程有一个5(4*5=20)数量的子列表,然后送入队列,求出取出的数量个数。老是不对。
 楼主| Cool_Breeze 发表于 2021-3-20 20:52
WolfAvenue 发表于 2021-3-20 20:03
你想要正确结果,好像是同步的吧,你代码逻辑有问题

GetExtraAdd 这个方法有问题, extraIdex 值大于 数组长度后 会一直返回 -1 那会导致其他线程退出了!
WolfAvenue 发表于 2021-3-20 20:52
本帖最后由 WolfAvenue 于 2021-3-20 20:54 编辑
Cool_Breeze 发表于 2021-3-20 20:36
恩,我也有感觉,好像是114 更 118 这几行!
Args.queue.TryDequeue(out temp) == false 如果成功在 Con ...

你这个是测试的Demo吧,有实际用途?而且输出结果还是同步的,你这种思路不对哦

那是个线程都是异步的啊,虽然有lock 也是阻塞,还是要排队。

代码
[C#] 纯文本查看 复制代码
 lock(Args.queue) // 只有保证这四个线程锁的是同一个引用对象就行
                {
                    if (Args.queue.TryDequeue(out temp) == false)
                    {
                        Args.queue.Enqueue(args.GetExtraAdd()); // 额外添加一点数据
                        continue;
                    }
                    if (temp == -1) break; // 线程结束
                }
                args.Add(); // 成功获取值以后Args.Count ++;


每一个线程处理队列元素  不一定是5个,  if (temp == -1) 达到这条件 ,可能线程处理队列元素5, 6,7,8。。。。都有可能 (因为先进了20个元素,一直在累加次数,然后并没有进GetExtraAdd())
 楼主| Cool_Breeze 发表于 2021-3-20 20:53
ykrne 发表于 2021-3-20 19:41
尽量不要用 Thread, 用 Task

恩。Thread 不好控制!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-26 05:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表