【问题标题】:which is more efficient threading.timer or task parallel library in the era of .net 4.5在 .net 4.5 时代哪个更高效 threading.timer 或任务并行库
【发布时间】:2017-06-26 11:39:26
【问题描述】:

我设计了一个应用程序(带有 .net 4.5 的 C#),它使用请求响应机制连接到多个启用以太网的硬件设备(最多 100 个设备),用于数据传输目的。

每个硬件设备都配置了唯一的 IP 地址和端口,我的应用程序使用 tcpclient 类连接并与该设备通信。

现在我的要求是,

  1. 使应用程序多线程,以便它能够同时连接所有设备。 (每个连接线程)
  2. 与所有这些设备发生的通信是重复的,即在每个特定时间间隔后,它应该连接到所有设备以实现 24X7 的数据传输目的。
  3. 对此我还有一个额外的要求,间隔应该在函数(回调函数)完成执行后配置。因为某些时间设备延迟了响应,所以我无法使用睡眠、延迟或等效功能。在这种情况下,即使我想要阻止的最后一次迭代没有完成,新的迭代也可能会开始。

即如果我连接到设备数据传输应该发生,并且只有在那之后它应该设置下一次执行的间隔。(很可能在回调函数本身)

为了实现这一点,我正在考虑使用 threading.timer,为此我可以在回调函数本身中设置线程间隔。我正在对此进行测试,并且最初似乎可以正常工作,并且我相信我可以进一步优化此代码以在生产环境中工作。

但是进一步谷歌我开始相信任务并行库更有效,因为我正在使用 .net 4.5 我应该使用这个而不是经典的 threading.timer 类。

但由于 TPL 没有与 threading.timer 完全等效的功能,我将不得不对其进行改进以完全符合我的要求。

所以我的问题是,如果我通过一些自定义而不是 threading.timer 来使用 TPL,我真的会获得任何性能提升,还是应该只继续使用 threading.timer。

【问题讨论】:

  • 使应用程序多线程化,以便它能够同时连接所有设备”是一种误解,会导致你走错路。使用异步 IO,而不是每个连接一个线程。
  • @spender 我明白了。虽然我不确定,但我相信即使我为每个连接使用线程,它也不会影响性能,因为我的连接有限(可能永远不会超过 100)
  • 没有线程怎么样? IO 在操作系统级别是异步的。模拟阻塞。通过使用 async/await,您可以在不使用任何线程的情况下使用本机异步功能。
  • @BoeseB async/await TPL 的一个特性。
  • @HarshalSam 你应该重新考虑你的方法。连接 100 个物联网设备的服务器是一个非常大的安全漏洞。如果设备可以收听,它就可以并且将会被黑客入侵。物联网设备太弱,无法安装强大的防火墙和反恶意软件防御。 “但它在公司网络内部”也不意味着它是安全的——它意味着任何受到攻击的公司 PC 都可以破解所有它们。

标签: c# multithreading timer task-parallel-library


【解决方案1】:

我建议在线程和 TPL 上使用 Microsoft 的 Reactive Framework (NuGet "System.Reactive")。这是我要做的:

首先定义一组TimeSpan 值,可用于更改每个设备的调度:

int devices = 3;

TimeSpan[] timerTimeSpans =
    Enumerable
        .Range(0, devices)
        .Select(x => TimeSpan.FromMilliseconds(2000))
        .ToArray();

我已经开始使用默认的 2000 毫秒。

然后定义一个从设备中读取的方法:

public Task<DeviceResult> DeviceFetchAsync(int device_number, Action<TimeSpan> reschedule)
{
    if (device_number == 1)
    {
        reschedule(TimeSpan.FromMilliseconds(10000));
    }

    return Task.Factory.StartNew(() => new DeviceResult()
    {
        DeviceNumber = device_number
    });
}

public class DeviceResult
{
    public int DeviceNumber;
}

它包含一个Action&lt;TimeSpan&gt; reschedule 来更改此特定设备的调度。

现在对于设置所有计时器并从设备读取数据的 Rx 查询:

IObservable<DeviceResult> query =
    Observable
        .Range(0, devices)
        .Select(n =>
            Observable
                .Generate(0, x => true, x => x + 1, x => x, x => timerTimeSpans[n])
                .SelectMany(x =>
                    Observable
                        .FromAsync(() => DeviceFetchAsync(n, ts => timerTimeSpans[n] = ts))))
        .Merge();

最后是让整个事情变得更糟的订阅:

IDisposable subscription =
    query
        .Subscribe(r => Console.WriteLine(r.DeviceNumber));

如果你想停止一切,就做subscription.Dispose();

就是这样。这就是所有的代码。我已经对此进行了测试,并且效果很好。

【讨论】:

    猜你喜欢
    • 2015-06-21
    • 1970-01-01
    • 2012-09-02
    • 2019-04-25
    • 2017-11-30
    • 2016-10-18
    • 1970-01-01
    相关资源
    最近更新 更多