[C#]タスクを非同期でシリアル処理をする(?)

.NetフレームワークにObjective-Cのdispatchシリアルキューみたいなの無いのかなぁ。見逃してる?

<span style=”text-decoration:line-through>lock使わずに書きたいけど、ConcurrentQueueとかInterlockedとか使って何とかならないかと考えたけど無理っぽい・・・? => ぜんぜん無理じゃなかった。

lock を使う版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/// <summary>
/// タスクを非同期でシリアル処理をするキュー。
/// インスタンスの生成がメンドイ場合はDefaultQueueを使って下さい。
/// SerialTaskQueue.DefaultQueue.Run(アクション)
/// </summary>
public class SerialTaskQueue
{
private static readonly SerialTaskQueue defaultQueue = new SerialTaskQueue();
private readonly Queue<Action> tasks = new Queue<Action>();
private bool active = false;

public static SerialTaskQueue DefaultQueue { get { return defaultQueue; } }

public void Run(Action action)
{

lock (tasks)
{
if (active)
{
tasks.Enqueue(action);
}
else
{
active = true;
Task.Run(action).ContinueWith(_ => RunTaskRecursive());
}
}
}

private void RunTaskRecursive()
{

lock (tasks)
{
if (tasks.Count == 0)
{
active = false;
}
else
{
Task.Run(tasks.Dequeue()).ContinueWith(_ => RunTaskRecursive());
}
}
}
}

Interlock を使う版

競合が多いと lock 版より速度落ちる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/// <summary>
/// タスクを非同期でシリアル処理をするキュー。
/// インスタンスの生成がメンドイ場合はDefaultQueueを使って下さい。
/// SerialTaskQueue.DefaultQueue.Run(アクション)
/// </summary>
public class SerialTaskQueue
{
private static readonly SerialTaskQueue defaultQueue = new SerialTaskQueue();
private readonly Queue<Action> tasks = new Queue<Action>();
private bool active = false;
private volatile int locked = 0;

public static SerialTaskQueue DefaultQueue { get { return defaultQueue; } }

public void Run(Action action)
{

int f = 1
try
{
do
{
f = Interlocked.CompareExchange(ref locked, 1, 0);
} while (f == 1)

if (active)
{
tasks.Enqueue(action);
}
else
{
active = true;
Task.Run(action).ContinueWith(_ => RunTaskRecursive());
}
}
finally
{
if (f == 0)
{
locked = 0;
}
}
}

private void RunTaskRecursive()
{

int f = 1
try
{
do
{
f = Interlocked.CompareExchange(ref locked, 1, 0);
} while (f == 1)

if (tasks.Count == 0)
{
active = false;
}
else
{
Task.Run(tasks.Dequeue()).ContinueWith(_ => RunTaskRecursive());
}
}
finally
{
if (f = 0)
{
locked = 0;
}
}
}
}
関連があるかもしれない記事