@@ -10,7 +10,7 @@ namespace dotnetCampus.Threading
10
10
/// 提供一个异步的队列。可以使用 await 关键字异步等待出队,当有元素入队的时候,等待就会完成。
11
11
/// </summary>
12
12
/// <typeparam name="T">存入异步队列中的元素类型。</typeparam>
13
- public class AsyncQueue < T > : IDisposable
13
+ public class AsyncQueue < T > : IDisposable , IAsyncDisposable
14
14
{
15
15
private readonly SemaphoreSlim _semaphoreSlim ;
16
16
private readonly ConcurrentQueue < T > _queue ;
@@ -36,6 +36,8 @@ public AsyncQueue()
36
36
/// <param name="item">要入队的元素。</param>
37
37
public void Enqueue ( T item )
38
38
{
39
+ ThrowIfDisposing ( ) ;
40
+
39
41
_queue . Enqueue ( item ) ;
40
42
_semaphoreSlim . Release ( ) ;
41
43
}
@@ -46,12 +48,15 @@ public void Enqueue(T item)
46
48
/// <param name="source">要入队的元素序列。</param>
47
49
public void EnqueueRange ( IEnumerable < T > source )
48
50
{
51
+ ThrowIfDisposing ( ) ;
52
+
49
53
var n = 0 ;
50
54
foreach ( var item in source )
51
55
{
52
56
_queue . Enqueue ( item ) ;
53
57
n ++ ;
54
58
}
59
+
55
60
_semaphoreSlim . Release ( n ) ;
56
61
}
57
62
@@ -63,6 +68,7 @@ public void EnqueueRange(IEnumerable<T> source)
63
68
/// 由于此方法有返回值,后续方法可能依赖于此返回值,所以如果取消将抛出 <see cref="TaskCanceledException"/>。
64
69
/// </param>
65
70
/// <returns>可以异步等待的队列返回的元素。</returns>
71
+ /// <exception cref="ObjectDisposedException"></exception>
66
72
public async Task < T > DequeueAsync ( CancellationToken cancellationToken = default )
67
73
{
68
74
while ( ! _isDisposed )
@@ -73,8 +79,19 @@ public async Task<T> DequeueAsync(CancellationToken cancellationToken = default)
73
79
{
74
80
return item ;
75
81
}
82
+ else
83
+ {
84
+ // 没有内容了,此时也准备gg了,那么就 Break 了
85
+ if ( _isDisposing )
86
+ {
87
+ _disposeTaskCompletionSource . TrySetResult ( true ) ;
88
+ break ;
89
+ }
90
+ }
76
91
}
77
92
93
+ ThrowIfDisposing ( ) ;
94
+
78
95
return default ;
79
96
}
80
97
@@ -85,13 +102,44 @@ public void Dispose()
85
102
{
86
103
// 当释放的时候,将通过 _queue 的 Clear 清空内容,而通过 _semaphoreSlim 的释放让 DequeueAsync 释放锁
87
104
// 此时将会在 DequeueAsync 进入 TryDequeue 方法,也许此时依然有开发者在 _queue.Clear() 之后插入元素,但是没关系,我只是需要保证调用 Dispose 之后会让 DequeueAsync 方法返回而已
88
- _isDisposed = true ;
105
+ _isDisposing = true ;
89
106
_queue . Clear ( ) ;
90
107
// 释放 DequeueAsync 方法
91
108
_semaphoreSlim . Release ( int . MaxValue ) ;
109
+ _isDisposed = true ;
92
110
_semaphoreSlim . Dispose ( ) ;
93
111
}
94
112
95
113
private bool _isDisposed ;
114
+
115
+ /// <summary>
116
+ /// 等待所有任务执行完成
117
+ /// </summary>
118
+ /// <returns></returns>
119
+ public async ValueTask DisposeAsync ( )
120
+ {
121
+ _disposeTaskCompletionSource = new TaskCompletionSource < bool > ( ) ;
122
+ _isDisposing = true ;
123
+
124
+ // 释放 DequeueAsync 方法
125
+ _semaphoreSlim . Release ( int . MaxValue ) ;
126
+
127
+ await _disposeTaskCompletionSource . Task ;
128
+
129
+ _isDisposed = true ;
130
+ _semaphoreSlim . Dispose ( ) ;
131
+ }
132
+
133
+ private bool _isDisposing ;
134
+ private TaskCompletionSource < bool > _disposeTaskCompletionSource ;
135
+
136
+ // 这里忽略线程安全
137
+ private void ThrowIfDisposing ( )
138
+ {
139
+ if ( _isDisposing )
140
+ {
141
+ throw new ObjectDisposedException ( nameof ( AsyncQueue < T > ) ) ;
142
+ }
143
+ }
96
144
}
97
- }
145
+ }
0 commit comments