对推送模型中的每一层使用 TaskFactory.StartNew 是一种好习惯吗?
Is it a good practice to use TaskFactory.StartNew for every layer in push model?
假设我有几层:
- 从套接字读取数据的管理器
- 订阅 #1 并负责持久化数据的管理器
- 订阅 #2 并负责数据反序列化并将其传播给对某些事件类型感兴趣的类型化管理器的管理器
- 显示数据的 WPF 控制器(已订阅 #3)
截至目前,我使用
TaskFactory.StartNew(()=>subscriber.Publish(data));
在每一层上。这样做的原因是我不 想要 相信每个经理都会快速完成他的工作,而那个前任。套接字管理器未卡住。
这是个好方法吗?
编辑
假设套接字管理器收到 价格更新
有 10 个管理器订阅了 Socket 管理器,所以当 Socket 管理器传播消息时。StartNew 被调用 10 次。
经理 #2、#3 除了通过 .StartNew 将消息传播给单个订阅者外什么都不做
所以最终每来自套接字 30x 的 1 条消息调用 .StartNew()。
关于 Task.Run
与 TaskFactory.StartNew
,它们在本质上是相同的。请阅读以下link:http://blogs.msdn.com/b/pfxteam/archive/2014/12/12/10229468.aspx
尽管这些方法使用 ThreadPool 以获得不错的性能,但仍然存在与不断即时创建新任务相关的开销。 Task
通常更多地用于不频繁的、即发即弃类型的工作负载。您关于“来自套接字的每 1 条消息 30x .StartNew()
”的陈述有点令人担忧。套接字消息多久到达一次?如果您真的很关心延迟,我认为更好的方法是每个管理器都应该有自己的专用线程。您可以使用 BlockingQueue 实现,以便线程等待使用父队列中的父输入项。例如,这比简单的自旋锁更可取。
这是金融市场消息订阅和解码中经常使用的架构,需要尽可能快的性能。还要记住,更多的线程并不总是等同于更快的性能。如果线程有任何共享数据依赖性,它们都将争用相同的锁,导致上下文相互切换等。这就是为什么预设数量的专用线程通常可以胜过创建更多线程的原因-苍蝇。我能想到的唯一例外是 "embarrassingly parallel" 根本没有共享数据依赖关系的任务。请注意,依赖项可以存在于输入端和输出端(任何有 lock
线程可以 运行 进入的地方)。
这似乎是一个合理的方法。
但是,如果可以有意义地做到:
subscriber.PublishAsync(data).LogExceptions(Log);
其中 LogExceptions
类似于:
// I'm thinking of Log4Net here, but of course something else could be used.
public static Task LogExceptions(this Task task, ILog log)
{
return task.ContinueWith(ta => LogFailedTask(ta, log), TaskContinuationOptions.OnlyOnFaulted);
}
private static void LogFailedTask(Task ta, ILog log)
{
var aggEx = ta.Exception;
if(aggEx != null)
{
log.Error("Error in asynchronous event");
int errCount = 0;
foreach(var ex in aggEx.InnerExceptions)
log.Error("Asynchronous error " + ++errCount, ex);
}
}
这样一劳永逸地使用任务仍然会记录错误,并且 PublishAsync
反过来在适当的地方使用任务,那么我会更开心。特别是,如果 "publishing" 有任何东西会阻塞可以用 async
处理的线程,比如写入或读取数据库或文件系统,那么线程的使用可以更好地扩展。
假设我有几层:
- 从套接字读取数据的管理器
- 订阅 #1 并负责持久化数据的管理器
- 订阅 #2 并负责数据反序列化并将其传播给对某些事件类型感兴趣的类型化管理器的管理器
- 显示数据的 WPF 控制器(已订阅 #3)
截至目前,我使用
TaskFactory.StartNew(()=>subscriber.Publish(data));
在每一层上。这样做的原因是我不 想要 相信每个经理都会快速完成他的工作,而那个前任。套接字管理器未卡住。
这是个好方法吗?
编辑 假设套接字管理器收到 价格更新 有 10 个管理器订阅了 Socket 管理器,所以当 Socket 管理器传播消息时。StartNew 被调用 10 次。
经理 #2、#3 除了通过 .StartNew 将消息传播给单个订阅者外什么都不做
所以最终每来自套接字 30x 的 1 条消息调用 .StartNew()。
关于 Task.Run
与 TaskFactory.StartNew
,它们在本质上是相同的。请阅读以下link:http://blogs.msdn.com/b/pfxteam/archive/2014/12/12/10229468.aspx
尽管这些方法使用 ThreadPool 以获得不错的性能,但仍然存在与不断即时创建新任务相关的开销。 Task
通常更多地用于不频繁的、即发即弃类型的工作负载。您关于“来自套接字的每 1 条消息 30x .StartNew()
”的陈述有点令人担忧。套接字消息多久到达一次?如果您真的很关心延迟,我认为更好的方法是每个管理器都应该有自己的专用线程。您可以使用 BlockingQueue 实现,以便线程等待使用父队列中的父输入项。例如,这比简单的自旋锁更可取。
这是金融市场消息订阅和解码中经常使用的架构,需要尽可能快的性能。还要记住,更多的线程并不总是等同于更快的性能。如果线程有任何共享数据依赖性,它们都将争用相同的锁,导致上下文相互切换等。这就是为什么预设数量的专用线程通常可以胜过创建更多线程的原因-苍蝇。我能想到的唯一例外是 "embarrassingly parallel" 根本没有共享数据依赖关系的任务。请注意,依赖项可以存在于输入端和输出端(任何有 lock
线程可以 运行 进入的地方)。
这似乎是一个合理的方法。
但是,如果可以有意义地做到:
subscriber.PublishAsync(data).LogExceptions(Log);
其中 LogExceptions
类似于:
// I'm thinking of Log4Net here, but of course something else could be used.
public static Task LogExceptions(this Task task, ILog log)
{
return task.ContinueWith(ta => LogFailedTask(ta, log), TaskContinuationOptions.OnlyOnFaulted);
}
private static void LogFailedTask(Task ta, ILog log)
{
var aggEx = ta.Exception;
if(aggEx != null)
{
log.Error("Error in asynchronous event");
int errCount = 0;
foreach(var ex in aggEx.InnerExceptions)
log.Error("Asynchronous error " + ++errCount, ex);
}
}
这样一劳永逸地使用任务仍然会记录错误,并且 PublishAsync
反过来在适当的地方使用任务,那么我会更开心。特别是,如果 "publishing" 有任何东西会阻塞可以用 async
处理的线程,比如写入或读取数据库或文件系统,那么线程的使用可以更好地扩展。