如何使用多线程输出0~int.MaxValue,不到十行代码困扰两天了

SunflowerLife 2019-03-10 10:51:12

static void Main(string[] args)
{
int j = 0;
for (int i = 0; i < int.MaxValue; i++)
{
new Task(() =>
{
Console.WriteLine(j++);
}).Start();
}
Console.ReadKey();
}

使用多线程则内存一直涨,直至内存溢出。线程终止后GC不会回收,强制回收不行,使用ThreadPool限制10个线程也是内存溢出。怎么感觉这线程的生命周期是整个进程呢?对于7*24小时运行的程序,怎么正确使用多线程呢?
...全文
1274 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
游北亮 2019-03-12
  • 打赏
  • 举报
回复
用Parallel
long t = 0;
Parallel.For(1, int.MaxValue, i => t += i);
Console.Write(t);
狂野的小强 2019-03-12
  • 打赏
  • 举报
回复
开10个线程去处理数据,别一直new线程,不然肯定爆炸。
zjy830110 2019-03-11
  • 打赏
  • 举报
回复
不错,很好的提案。
  • 打赏
  • 举报
回复
“系统服务会卡死,导致性能下降”是程序逻辑设计问题,根本不是绕着套一点底层基本编程代码就能“解决”的!
  • 打赏
  • 举报
回复
我们用正常的测试用例,用 Task.Run 注册1千万个任务看看效果,就够了。而 lz 必须研究“到火星上怎样用溪水洗净黄瓜上的泥”的问题而不管自己有没有可能上火星去找溪水的问题。
  • 打赏
  • 举报
回复
引用 14 楼 我是小小熊 的回复:
我认为楼主走进误区了 1、使用多线程输出0~int.MaxValue ,不一定要使用int.MaxValue个Task/线程
“正常情况下”,lz 仍然要古易走极端的。
我是小小熊 2019-03-11
  • 打赏
  • 举报
回复
我认为楼主走进误区了 1、使用多线程输出0~int.MaxValue ,不一定要使用int.MaxValue个Task/线程 2、Task: 任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。 任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制. 3、个人认为线程数最好不要超过CPU的线程数->Task不要超多线程*4 (这里需要注意集中计算Task多一点要好,系统分配的资源更多,但是不要特别多,不要超多元系统中的3倍(个人在服务器上的经验,不是标准),不然系统服务会卡死,导致性能下降) :Task转到线程和线程切换都需要资源。 讲到这里就是人物的分配和资源转换问题了,个人比较喜欢用Parallel
   Parallel.For(0, int.MaxValue, (i) =>
            {
                Console.WriteLine($"{i}, task{Task.CurrentId},thread:{Thread.CurrentThread.ManagedThreadId}");
            }
            );
            Console.ReadKey();
exception92 2019-03-11
  • 打赏
  • 举报
回复
。net framework 引入Task概念之后就不需要手动去管理线程了,线程池帮我们做这些事情,线程执行任务完毕之后还会存在一段时间,直到被分配的新任务唤醒或者到一定时间进行销毁。输出区间值这样就可以了,用多线程有点浪费性能的感觉。
var j = i;
Console.Write("{0}\t", j);
wanghui0380 2019-03-11
  • 打赏
  • 举报
回复
你思维陷入误区了,你这里并不是什么不能释放的问题。你这里是new 比释放的快,比释放多

一个水池,一边进水,一边出水---------如果进水是出水的20倍,那么会怎么样。
进水是new,出水是释放,所以不是不能释放,发洪水了,来不及排出去
wanghui0380 2019-03-11
  • 打赏
  • 举报
回复
Task.Run(() =>
{
foreach (var i in _blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine(i);
}
});

使用的
Task.Run(() =>
{
foreach (var i in _blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine("1:"+i);
}
});
Task.Run(() =>
{
foreach (var i in _blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine("2:"+i);
}
});

2个消费也没关系,其实从2次消费,你就可以明显看到生产和消费的情况了,如果生产和消费持平,那么理论上至少是大概率1,2交替执行。但是你可以看到的是实际是“成批交替”

当然这只是基本手段,处理并发,处理限流,处理分流,手段多的很。哪怕是看似很高深的东西,其实都是最基本的思维方式。比如最近考官最喜欢考的“一致性hash”,你这里不能用么。一样用5个限流池,一个“一致性hash”做的“分流导管”,这样也行。
SunflowerLife 2019-03-11
  • 打赏
  • 举报
回复
使用多线程,不能正确释放线程所占的内存,程序就会越来越大。如果使用单线程,并发不好处理不够友好;隔一段时间自动重启程序,又怕出问题;如果使用多进程或者IIS作为宿主的WAPAPI、WCF或许也可以。但是现在弄不明白多线程如何正确释放,好尴尬......
SunflowerLife 2019-03-11
  • 打赏
  • 举报
回复
引用 8 楼 wanghui0380 的回复:
那是因为你的生产大于消费,所以内部task堆积了。
所以需要限流,我这里采用BlockingCollection<int> 就能看到明显效果

   static void Main(string[] args)
{
BlockingCollection<int> _blockingCollection=new BlockingCollection<int>(10000);
int j = 0;
Task.Run(() =>
{
foreach (var i in _blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine(i);
}
});
for (int i = 0; i < int.MaxValue; i++)
{
_blockingCollection.Add(j++);

}
Console.ReadKey();
}
}

如果打印之前加个Thread.Sleep(500),那么是隔半秒钟打印一个数字。也就是说,还是感觉只是多开了一个线程执行。目前是服务器上有个Windows服务,越用越卡。现在怎么使用多线程,并且能够正确释放线程所占用的内存?
wanghui0380 2019-03-11
  • 打赏
  • 举报
回复
那是因为你的生产大于消费,所以内部task堆积了。
所以需要限流,我这里采用BlockingCollection<int> 就能看到明显效果

   static void Main(string[] args)
{
BlockingCollection<int> _blockingCollection=new BlockingCollection<int>(10000);
int j = 0;
Task.Run(() =>
{
foreach (var i in _blockingCollection.GetConsumingEnumerable())
{
Console.WriteLine(i);
}
});
for (int i = 0; i < int.MaxValue; i++)
{
_blockingCollection.Add(j++);

}
Console.ReadKey();
}
}
SunflowerLife 2019-03-11
  • 打赏
  • 举报
回复
引用 2 楼 以专业开发人员为伍 的回复:
[quote=引用 楼主 SunflowerLife 的回复:]使用多线程则内存一直涨,直至内存溢出。线程终止后GC不会回收,强制回收不行,使用ThreadPool限制10个线程也是内存溢出。怎么感觉这线程的生命周期是整个进程呢?对于7*24小时运行的程序,怎么正确使用多线程呢?


刚学点“多线程”概念的人自然是只知道
new Thread();
这类代码。

实际上根本不用自己遍代码创建什么线程对象。应该使用 .net 线程池框架自动化地管理和调度。[/quote]


static void Test()
{
ThreadPool.SetMaxThreads(100, 100);
for (int i = 0; i < int.MaxValue; i++)
{
ThreadPool.QueueUserWorkItem(Print, i);
}
}
static void Print(object i)
{
Console.WriteLine(i);
}

还是报内存溢出,线程池基本没用过,也不知用得对不对
SunflowerLife 2019-03-11
  • 打赏
  • 举报
回复
引用 3 楼 liusa1997 的回复:
[quote=引用 楼主 SunflowerLife 的回复:]

static void Main(string[] args)
{
int j = 0;
for (int i = 0; i < int.MaxValue; i++)
{
new Task(() =>
{
Console.WriteLine(j++);
}).Start();
}
Console.ReadKey();
}

你每次new一个对象用了也不释放,然后循环完就new了maxvalue这么多个,最后内存就满了。要不直接运行tast.run,要不就用了就释放掉[/quote]
Task.Run也是报内存溢出,如果使用Task.Wait(),Task.Dispose()这样又跟单线程一样,感觉不会用多线程了

static void Test()
{
for (int i = 0; i < int.MaxValue; i++)
{
Task task = new Task(Print, i);
task.Start();
task.Wait();
task.Dispose();
}
}
static void Print(object i)
{
Thread.Sleep(500);//每半秒打印一个
Console.WriteLine(i);
}
SunflowerLife 2019-03-11
  • 打赏
  • 举报
回复
引用 1 楼 以专业开发人员为伍 的回复:
static void Main(string[] args)
{
for (int i = 0; i < int.MaxValue; i++)
{
var j = i;
Task.Run(() =>
{
Console.Write("{0}\t", j);
});
}
Console.WriteLine(".......按任意键退出");
Console.ReadKey();
}

Task.Run也是报内存溢出
OrdinaryCoder 2019-03-11
  • 打赏
  • 举报
回复
引用 3 楼 liusa1997 的回复:
[quote=引用 楼主 SunflowerLife 的回复:]

static void Main(string[] args)
{
int j = 0;
for (int i = 0; i < int.MaxValue; i++)
{
new Task(() =>
{
Console.WriteLine(j++);
}).Start();
}
Console.ReadKey();
}

你每次new一个对象用了也不释放,然后循环完就new了maxvalue这么多个,最后内存就满了。要不直接运行tast.run,要不就用了就释放掉[/quote]
你函数没退出,task对象的一直在被引用,当然会出现内存溢出的情况。
SoulRed 2019-03-11
  • 打赏
  • 举报
回复
你开了 21亿个任务。。。 这个级别的处理量。目前不崩溃只有专业服务器
小小柠檬檬 2019-03-11
  • 打赏
  • 举报
回复
1、使用多线程输出0~int.MaxValue ,不一定要使用int.MaxValue个Task/线程
2、Task:

讲到这里就是人物的分配和资源转换问题了,个人比较喜欢用Parallel
SunflowerLife 2019-03-11
  • 打赏
  • 举报
回复
引用 16 楼 以专业开发人员为伍 的回复:
我们用正常的测试用例,用 Task.Run 注册1千万个任务看看效果,就够了。而 lz 必须研究“到火星上怎样用溪水洗净黄瓜上的泥”的问题而不管自己有没有可能上火星去找溪水的问题。
谢谢您的回答!咱不是要钻牛角尖,现在多线程里面只是执行Console.Write,实际情况新开的一个线程做更多的事情,内存会不会更大,到时不是1千万,而是1来万就有问题了呢。简单的例子,都弄不明白原因,怎么能写更好的程序呢,是吧
加载更多回复(5)

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧