英文:
How can I invoke a function at special position after receiving outer special message in C#?
问题
让我们看一下代码段:csharp
public async Task TestMethod(){
//做一些事情
string a = await WaitingObj.Read();
//继续做一些事情
}
我们有一个消息队列,它一直在接收消息。当它发现消息是TestMethod()
需要的时候,它会将此消息传递给WaitingObj.Read()
,后者将返回该值并将其传递给string a
。
然而,我们知道我们不能两次调用Read()
(一次在TestMethod()
中传递值,另一次在队列接收到新消息时判断消息是否是TestMethod()
所需的)。
那么,我应该如何使用Await/Async解决这个问题或者设计程序?
问题中的消息队列只是基本数据结构中的简单队列结构。
WaitingObj.Read()函数的功能只是指示何时可以将数据传递给string a
,而string a
可以直接使用它并继续执行其余的代码。
英文:
Let us look at a segment of a code: csharp
public void TestMethod(){
//do something
string a = await WaitingObj.Read();
//continue to do something
}
And we have a message queue, which is receiving messages all the time. When it finds the message is what the TestMethod()
needs, it will pass this message to WaitingObj.Read()
which will return the value and pass the value to the string a
.
However, We know we cannot invoke Read()
twice (in the TestMethod()
to pass the value and when the queue receives new messages to judge whether the message is the TestMethod()
needs).
So, How can I solve this problem with Await/Async or design the programs.
The message queue in the problem is just a simple queue structure in basic data structure.
The function of WaitingObj.Read() is just indicating when the data is ready for passing to string a
and string a
can directly use it can continue carrying the rest of codes.
答案1
得分: 1
在阅读了您的帖子和所有评论后,我特别注意到您提到的部分:
"我想知道是否可以通过设计WaitingObj.Read()来解决问题..."
让我们通过设计一个实现了INotifyCollectionChanged
接口的Queue
来考虑一下这个想法,并提供以下功能:
- 一个
ReadAsync
方法,用于等待与指定谓词匹配的"特殊"消息。 - 一个
SelfTest
方法,从10个消息的列表中每秒入队一个消息。
然后,可以在控制台应用程序中使用var WaitingObj = new DesignedObservableQueue()
的实例来测试是否满足您的设计规范。
设计的队列(也称为"WaitingObj")如下所示:
class DesignedObservableQueue : Queue<MockMessage>, INotifyCollectionChanged
{
public new void Enqueue(MockMessage message)
{
base.Enqueue(message);
CollectionChanged?
.Invoke(
this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
message));
}
public new MockMessage Dequeue()
{
var message = base.Dequeue();
CollectionChanged?
.Invoke(
this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove,
message));
return message;
}
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public async Task ReadAsync(Predicate<MockMessage> condition)
{
var awaiter = new SemaphoreSlim(0, 1);
try
{
CollectionChanged += localOnCollectionChanged;
await awaiter.WaitAsync();
}
finally
{
awaiter.Release();
CollectionChanged -= localOnCollectionChanged;
}
void localOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
var message = e.NewItems!.Cast<MockMessage>().First();
if (condition(message))
{
Console.WriteLine($"MATCH: {message.Message}");
awaiter.Release();
}
else
{
Console.WriteLine($"NO MATCH: {message.Message}");
}
break;
default:
break;
}
}
}
public async Task SelfTest(CancellationToken token)
{
foreach (
var message in new[]
{
"occasion",
"twin",
"intention",
"arrow",
"draw",
"forest",
"special",
"please",
"shell",
"momentum",
})
{
if (token.IsCancellationRequested) return;
Enqueue(new MockMessage { Message = message });
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
执行测试方法时,将您帖子中显示的TestMethod
更改为异步方法,然后执行以下最小测试:
static void Main(string[] args)
{
Console.Title = "Test Runner";
var stopwatch = new Stopwatch();
var WaitingObj = new DesignedObservableQueue();
// 本地测试方法期望在约6秒内匹配谓词,所以允许10秒。
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
stopwatch.Start();
_ = WaitingObj.SelfTest(cts.Token);
try
{
TestMethod().Wait(cts.Token);
Console.WriteLine($"PASSED {stopwatch.Elapsed}");
}
catch (OperationCanceledException ex)
{
Console.WriteLine($"FAILED {stopwatch.Elapsed}");
}
// 本地测试方法
async Task TestMethod()
{
// 做一些事情
await WaitingObj.ReadAsync((message) => message.Message == "special");
// 继续做一些事情
}
Console.ReadKey();
}
其中:
class MockMessage
{
public string Message { get; set; } = string.Empty;
}
这是您提供的内容的翻译部分。
英文:
After reading through your post and all the comments, I noticed in particular where you said:
>I wonder if I can just solve it by designing WaitingObj.Read()....
Let's entertain that thought by designing a Queue
that provides some basic observability by implementing INotifyCollectionChanged
and provides these features:
- A
ReadAsync
method to await a "special" message that matches a specified predicate. - A
SelfTest
method that enqueues one message per second from a list of 10 messages.
An instance of var WaitingObj = new DesignedObservableQueue()
can then be exercised in a console app to see whether or not this would satisfy your design specs.
Designed Queue (a.k.a. "WaitingObj")
class DesignedObservableQueue : Queue<MockMessage>, INotifyCollectionChanged
{
public new void Enqueue(MockMessage message)
{
base.Enqueue(message);
CollectionChanged?
.Invoke(
this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
message));
}
public new MockMessage Dequeue()
{
var message = base.Dequeue();
CollectionChanged?
.Invoke(
this,
new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove,
message));
return message;
}
public event NotifyCollectionChangedEventHandler? CollectionChanged;
Provide a way to detect that a special message has been enqueued.
public async Task ReadAsync(Predicate<MockMessage> condition)
{
var awaiter = new SemaphoreSlim(0, 1);
try
{
CollectionChanged += localOnCollectionChanged;
await awaiter.WaitAsync();
}
finally
{
awaiter.Release();
CollectionChanged -= localOnCollectionChanged;
}
void localOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
var message = e.NewItems!.Cast<MockMessage>().First();
if(condition(message))
{
Console.WriteLine($"MATCH: {message.Message}");
awaiter.Release();
}
else
{
Console.WriteLine($"NO MATCH: {message.Message}");
}
break;
default:
break;
}
}
}
Mock a queue that "is receiving messages all the time" by self-enqueuing at one-second intervals.
public async Task SelfTest(CancellationToken token)
{
foreach (
var message in new[]
{
"occasion",
"twin",
"intention",
"arrow",
"draw",
"forest",
"special",
"please",
"shell",
"momentum",
})
{
if(token.IsCancellationRequested) return;
Enqueue(new MockMessage { Message = message });
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
Exercise TestMethod
Once the TestMethod
shown in your post is changed to an async
method, perform this minimal test:
static void Main(string[] args)
{
Console.Title = "Test Runner";
var stopwatch = new Stopwatch();
var WaitingObj = new DesignedObservableQueue();
// Local test method is expecting to match
// the predicate in ~6 seconds so allow 10.
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
stopwatch.Start();
_ = WaitingObj.SelfTest(cts.Token);
try
{
TestMethod().Wait(cts.Token);
Console.WriteLine($"PASSED {stopwatch.Elapsed}");
}
catch (OperationCanceledException ex)
{
Console.WriteLine($"FAILED {stopwatch.Elapsed}");
}
// Local test method
async Task TestMethod()
{
// do something
await WaitingObj.ReadAsync((message) => message.Message == "special");
// continue to do something
}
Console.ReadKey();
}
Where:
class MockMessage
{
public string Message { get; set; } = string.Empty;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论