提问人:user2896152 提问时间:8/1/2020 更新时间:8/1/2020 访问量:152
将回调传播到单独的线程上
Propagate a callback onto a separate thread
问:
我有一个关于C#的问题。multithreading
假设我们有这个接口
public interface IMyCallback
{
void OnSum(int a, int b, int result);
}
然后我还有另一个类,它有一个作为私有字段的列表IMyCallback
public class MyClass
{
private IList<IMyCallback> _Subscribers = new List<IMyCallback>();
public void Sum(int a, int b)
{
int result = a + b;
foreach (IMyCallback subscriber in _Subscribers)
{
subscriber.OnSum(a, b, result);
}
}
}
实际上,它所做的是在两个数字之间求和,并通知所有服务订阅者它已经执行了算术运算,结果就是结果。a + b
现在我想提高性能并将每个通知发送到线程上。为了避免编译器的外壳和内存问题,我通常做的是这样的:
public void Sum (int a, int b)
{
int result = a + b;
foreach (IMyCallback subscriber in _Subscribers)
{
new Thread((o) =>
{
object[] state = (object[])o;
((IMyCallback)state[0])
.OnSum((int)state[1], (int)state[2], (int)state[3]
})
{
IsBackground = true
}
.Start(new object[] { subscriber, a, b, result });
}
}
我想把注意力集中在最后一部分,当我将所有参数传递到一个对象数组中并将其用于委托时。我做对了吗?这是一个正确的模式还是有一些我无法理解的问题?
答:
3赞
Andy
8/1/2020
#1
我对你的问题添加了评论,但会尽量重申我所说的话。首先:关于将参数数组作为泛型对象传递的问题完全没问题。因为该方法是使用该泛型对象数组的唯一方法,所以它非常有意义。
我有点担心的一件事是你如何生成线程。如果您有 10,000 个订阅者,那么 CPU 会很粗糙。由于您当前使用的是同步代码,因此我会在您的订阅者上使用 a。Parallel.ForEach()
public void Sum (int a, int b)
{
var result = a + b;
Parallel.ForEach (_Subscribers, subscriber =>
{
subscriber.OnSum(a,b,result);
});
}
这样做的好处是,框架决定了完成工作所需的线程数。如果您有 5 个订阅者或 10,000 个订阅者,它不会杀死您的 CPU。
这段代码的美妙之处在于,如果抛出异常,调用者实际上会得到它。使用当前方法,所有异常将永远丢失。OnSum
伊塔:
现在,如果您想使用 async/await 和 ,这是安全执行此操作的一种方法:Task
public Task SumAsync (int a, int b)
{
return Task.WhenAll(_Subscribers.Select(x=> Task.Run(() =>
{
x.OnSum(a, b, a + b);
})));
}
这也将捕获/抛出所有异常。我不喜欢这个解决方案,因为它不是有机异步的。我喜欢使用这个解决方案。Parallel.ForEach()
评论
Parallel.ForEach()
event
Task