如何高效地按顺序运行C#任务

4

我正在尝试找到一种按顺序运行任务的方法。目前我想到的解决方案如下,但我怀疑它可以更简单/更高效地完成。

public class SequentialTaskRunner
{
    private ISet<Task> runningTasks = new HashSet<Task>();

    public void Run(Task task)
    {
        lock (runningTasks)
        {
            var tasksToAwait = runningTasks.ToArray();

            // create a task that waits for the currently running tasks
            var waitingTask = Task.Factory.StartNew(() =>
            {
                Task.WaitAll(tasksToAwait);
            });

            // make sure the task gets removed from the running tasks on completion
            task.ContinueWith((antecedent2) =>
            {
                lock (runningTasks)
                {
                    runningTasks.Remove(task);
                }
            });

            // prepend the waiting task 
            waitingTask.ContinueWith((antecedent) => task.Start());

            runningTasks.Add(task);
        }
    }
}

我想用这个工具来排队处理与com端口连接相关的任务,以防它们互相干扰。
情况如下:我正在使用一个RFID设备。有两个驱动程序在运行。一个用于读/写,另一个可以按请求扫描可用的com端口以查找另一个读取器。有时,将使用读取器驱动程序进行端口扫描以恢复连接。正是这两个“端口扫描”任务,我不希望同时发生。
我希望这样能够澄清我为什么想要两个任务不同时运行的情况。现在,可能还有另一种方法可以避免回答这个问题。但我仍然很好奇一个好的解决方案是什么 :)

所以,您想同步运行任务?那你是怎么陷入这种境地的?你能退出并只使用操作吗? - user1228
这是一个很好的问题。我会编辑我的帖子,稍微详细解释一下。 - bvh
2个回答

4

(评论原帖已被删除).

我的新理解是您有三种类型的任务。

  1. 读取器
  2. 写入器
  3. 端口扫描器

您希望这三个任务尽可能并行,但如果读取器选择在端口扫描重新连接时跳入,则不会在端口扫描器仍在运行时执行此操作。解决这种情况的一种方法是使用信号量(Semaphore)。信号量控制对有限资源的访问。

在您的情况下,您只有一个可用的有限资源(即端口扫描)。在这种情况下,我们可以选择使用一个更简单的AutoResetEvent。但我认为使用Semaphore实际上可以减少混淆。

// Only 1 task may port scan at a time
Semaphore portScanResource = new Semaphore(initialCount: 1, maximumCount: 1);

// ...

// "Reader task"
var task = Task.Factory.StartNew( () =>
    {
        // ...
        if (shouldPortScan)
        {
            portScanResource.WaitOne();
            try
            {
                // do your port scan
            }
            finally
            {
                // we're done
                portScanResource.Release();
            }
        }
    });

端口扫描任务将使用相同的Semaphore,确保只有1个线程同时执行端口扫描。


如果我理解正确的话,ConcurrentBag 不允许我从 runningTasks 中撤回“task”。如 @anvarbek 建议的那样,我正在查看 ConcurrentQueue 是否适合,因为“完成任务”应该始终是出队的任务,这可能起作用。 - bvh
@bvh:你的编辑使得这更加清晰。我正在考虑中。如果我想不出更好的解决方案,我就会删除这个答案。 - user7116
看着我的示例代码,我发现方法名“Run”有点令人困惑。其他选择可以是“Enqueue”或“Schedule”。后者表明制作调度程序也是一个有趣的选项。 - bvh
我会尝试一下,这绝对是更加微妙的方式来实现“排队”行为。 - bvh
我将其标记为答案,因为它解决了我的“原始”问题。感谢您的思考! - bvh
@bvh:不客气。有时候,特别是在线程方面,很难沟通所需的行为 :) - user7116

0
如果你使用的是C# 4,你可以使用ConcurrentQueue类来保证队列的线程安全性。
另外,你也可以查看其他的Concurrent*类。并行扩展也非常方便,但需要根据你的需求进行调整。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接