我是线程新手,我需要澄清以下情况。
我正在研究苹果推送通知服务。我的应用程序要求在向网站添加新交易时向30k用户发送通知。
我可以将30k用户拆分成列表,每个列表包含1000个用户并启动多个线程或者可以使用任务吗?
以下方式有效吗?
if (lstDevice.Count > 0)
{
for (int i = 0; i < lstDevice.Count; i += 2)
{
splitList.Add(lstDevice.Skip(i).Take(2).ToList<DeviceHelper>());
}
var tasks = new Task[splitList.Count];
int count=0;
foreach (List<DeviceHelper> lst in splitList)
{
tasks[count] = Task.Factory.StartNew(() =>
{
QueueNotifications(lst, pMessage, pSubject, pNotificationType, push);
},
TaskCreationOptions.None);
count++;
}
QueueNotification方法将遍历每个列表项并创建一个有效负载,如
foreach (DeviceHelper device in splitList)
{
if (device.PlatformType.ToLower() == "ios")
{
push.QueueNotification(new AppleNotification()
.ForDeviceToken(device.DeviceToken)
.WithAlert(pMessage)
.WithBadge(device.Badge)
);
Console.Write("Waiting for Queue to Finish...");
}
}
push.StopAllServices(true);
答案 0 :(得分:5)
从技术上讲,确定可以拆分列表,然后启动并行运行List的线程。您也可以像以前一样自己实现所有内容,但这不是一个好方法。首先将List拆分为并行处理的块已经是Parallel.For
或Parallel.ForEach
所做的事情。没有必要自己重新实现一切。
现在,您不断询问是否可以并行运行300或500个通知。但实际上这不是一个好问题,因为你完全错过了并行运行的重点。
所以,让我解释一下为什么这个问题不好。首先,你应该问自己为什么要并行运行?答案是,您希望通过使用多个CPU核心来运行更快的东西。
现在你的简单想法可能是产生300或500个线程的速度更快,因为你有更多线程并且它“并行”运行更多东西。但事实并非如此。
首先,创建一个线程不是“免费”的。您创建的每个线程都有一些开销,创建一个线程需要一些CPU时间,而且还需要一些内存。最重要的是,如果你创建300个线程,它并不意味着并行运行300个线程。如果你有一个8核CPU,那么只有8个线程可以并行运行。创建更多线程甚至可能会损害您的性能。因为现在你的程序需要在线程之间切换constanlty,这也会降低CPU的性能。
所有这一切的结果。如果你有轻量级的一些小代码,它们不会进行大量计算,那么它会导致创建大量线程会降低应用程序速度而不是更快地运行,因为管理线程会产生比运行它更多的开销(对于例子)8个cpu-cores。
这意味着,如果你有一个30,000的列表。它通常会以更快的方式将您的列表拆分为8个块,并在8个线程中通过您的列表来创建300个线程。
你的目标永远不应该是:它能并行运行xxx吗? 问题应该是:我需要多少线程,每个线程应该处理多少项目以使我的工作最快完成。
这是一个重要的区别,因为只是产生更多的线程并不意味着某些东西最终会成功。
那么你需要多少线程,每个线程应该处理多少项?好吧,你可以编写很多代码来测试它。但是数量从硬件变为硬件。只有4个核心的PC比具有8个核心的系统具有另一个优化。如果你正在做的是IO绑定(例如读/写到磁盘/网络),你也不会通过增加线程来获得更快的速度。
所以你现在可以做的就是测试一切,尝试获得正确的线程编号并进行大量的基准测试以找到最佳数字。
但实际上,这就是具有Task<T>
类的TPL库的全部目的。 Task<T>
类已经在您的计算机上查看它拥有多少个cpu核心。当您运行任务时,它会自动尝试创建所需的线程,以便最大限度地利用您的系统。
所以我的建议是你应该使用带有Task<T>
类的TPL库。在我看来,你不应该自己直接创建线程或自己做分区,因为所有这些都已在TPL中完成。
答案 1 :(得分:0)
我认为Task
- Class对你的目标来说是一个很好的选择,因为你可以轻松处理异步过程并且不必直接处理Threads。
也许这有帮助:Task vs Thread differences
但为了给你一个更好的答案,你应该改进你的问题,给我们更多细节。
创建多个并行线程时应该小心,因为这会降低应用程序的速度。阅读SO中的这篇好文章:How many threads is too many?。最好的是你可以配置它,而不是测试一些值。
答案 2 :(得分:0)
我同意Task
是一个不错的选择,但创建太多任务也会给您的系统和失败带来风险,您的决定也是提出解决方案的一个因素。对我来说,我更喜欢MSQueue与线程池相结合。
答案 3 :(得分:0)
如果您希望并行创建推送通知并使用计算机上的所有CPU来最大限度地提高性能,则应使用Parallel.ForEach
:
Parallel.ForEach(
devices,
device => {
if (device.PlatformType.ToUpperInvariant() == "IOS") {
push.QueueNotification(
new AppleNotification()
.ForDeviceToken(device.DeviceToken)
.WithAlert(message)
.WithBadge(device.Badge)
);
}
}
);
push.StopAllServices(true);
这假定调用push.QueueNotification
是线程安全的。此外,如果此调用锁定共享资源,则由于锁争用,您可能会看到低于预期的性能。
为避免此锁定争用,您可以为Parallel.ForEach
创建的每个分区创建单独的队列。我在这里即兴创作,因为问题中缺少一些细节。我假设变量push
是Push
类型的实例:
Parallel.ForEach(
devices,
() => new Push(),
(device, _, push) => {
if (device.PlatformType.ToUpperInvariant() == "IOS") {
push.QueueNotification(
new AppleNotification()
.ForDeviceToken(device.DeviceToken)
.WithAlert(message)
.WithBadge(device.Badge)
);
}
return push;
},
push.StopAllServices(true);
);
这将为Push
创建的每个分区创建一个单独的Parallel.ForEach
实例,当分区完成时,它将在实例上调用StopAllServices
。
这种方法应该不会比将设备分成N个列表更糟糕,其中N是CPU的数量,并且开始N个线程或N个任务来处理每个列表。如果一个线程或任务&#34;落后&#34;总执行时间将是这个&#34;慢&#34;的执行时间。线程或任务。使用Parallel.ForEach
,所有CPU都被使用,直到所有设备都被处理完毕。