C#套接字Async vs Mulithreading

时间:2014-12-30 14:45:27

标签: c# multithreading sockets asynchronous

我正在开展一个项目,我将从多个服务器(少于1000个)不断提取信息,并将大部分信息写入数据库。我把选择范围缩小到2:

编辑:这是一个客户端,因此我将定期生成连接和请求信息。

1 - 使用异步方法,创建N个套接字以进行轮询,确定是否将信息写入回调中的数据库并将有用信息放入缓冲区。然后使用计时器从缓冲区中写入信息。

2 - 使用多线程方法,创建N个线程,每个线程有一个套接字。有用信息的缓冲区将保留在主线程上,循环写入也将保留。

两个选项实际上都使用多个线程,只有第二个选项似乎增加了手动创建每个线程的额外难度。它有什么优点吗?是否明智地使用计时器进行写作?

4 个答案:

答案 0 :(得分:6)

使用1000个连接异步IO通常是一个好主意,因为它在IO正在进行时不会阻塞线程。 (它甚至不使用后台线程等待。)这使得(1)成为更好的选择。

从问题中不清楚你需要一个计时器。也许是为了缓冲写入?这是有效的,但似乎与这个问题无关。

轮询在现代异步IO应用程序中没有位置。完成后,系统会调用您的回调(或完成您的IO Task)。回调排队到线程池。这让你不用担心。它刚刚发生。

读取数据的代码应如下所示:

while (true) {
 var msg = await ReadMessageAsync(socket);
 if (msg == null) break;
 await WriteDataAsync(msg);
}

很简单。没有阻塞线程。没有回调。

答案 1 :(得分:1)

回答"正在使用计时器"问题,也许最好让缓冲区在达到某个特定时间或特定大小时自动刷新。这是内存缓存在.NET框架中的工作方式。缓存设置为最大大小和最大失效。

失败时的灵活性可能是一个问题,以及峰值负载如果是内存中的话可能会使缓冲区爆炸的可能性。您可以考虑使缓冲区本地但持久 - 例如使用MSMQ或类似的高速队列技术。我已经看到这个成功完成了,特别是如果你让缓冲区写入异步(即#34; fire and forget")它对服务输入队列的能力几乎没有影响,并允许数据库填充代码在需要时或在需要时从持久缓冲区中提取。

答案 2 :(得分:1)

另一个选择是拥有一个专用线程,其唯一的工作是为缓冲区提供服务并尽快将数据写入数据库。因此,当您建立连接并获取数据时,该数据将被放入缓冲区。但是你有一个线程总是在查看缓冲区并将数据写入数据库,因为它来自其他连接。

将缓冲区创建为BlockingCollection< T >。使用上一个答案中建议的异步请求。并有一个专用线程来读取数据并将其写入数据库:

BlockingCollection<DataType> _theQueue = new BlockingCollection<DataType>(MaxBufferSize);

// add data with
_theQueue.Add(Dataitem);

// service the queue with a simple loop
foreach (var dataItem in _theQueue.GetConsumingEnumerable())
{
    // write dataItem to the database
}

如果要关闭(即不再从服务器读取数据),则将队列标记为完成以进行添加。然后消费者线程将清空队列,注意它被标记为完成添加,循环将退出。

// mark the queue as complete for adding
_theQueue.CompleteAdding();

您需要使缓冲区足够大以处理突发信息。

如果一次向数据库写一条记录的速度不够快,你可以修改消费者循环,用一些记录(10?100?1000?)填充自己的内部缓冲区,并将它们写入数据库一次性完成。你如何做到这一点当然取决于你的服务器。但是你应该能够提出某种形式的批量插入,这将减少你对数据库的往返次数。

答案 3 :(得分:0)

对于选项(1),您可以将合格信息写入队列,然后使用数据库编写器监听队列。这将使您的数据库在峰值负载期间有一些喘息空间,并避免等待计时器的备份请求。

持久队列也会给你一些弹性。