我正在用 C# 创建一个 Discord 机器人,它显示队列中的用户列表。该列表每 30 秒更新一次。
我有两个线程,主线程和我创建的线程。在主线程中,我绑定了一个按钮按下事件处理程序,在该处理程序上绑定了异步方法。在我创建的线程中,我绑定了一个异步队列更新方法,该方法的延迟为 30 秒,并且应该每 30 秒执行一次。问题是两个线程都与相同的资源(静态字典)交互,这可能导致不正确的数据更改。
我想使用锁构造,但我认为更新队列的线程必须等待很长时间才能进入锁块,这意味着队列不会每 30 秒更新一次。
请帮我解决这个问题,我是编程新手!
private async void button10_Click(object sender, EventArgs e)
{
new Thread(async () =>
{
while (true)
{
await UpdateQueue();
await Task.Delay(30000);
}
}).Start();
channel = await discord.GetChannelAsync(queueChannelID);
discord.ComponentInteractionCreated += Discord_ButtonIsPressed;
await discord.ConnectAsync();
await Task.Delay(-1);
}
private async Task UpdateQueue()
{
queue.Add("test", "test"); // this is where new items are added to the queue from the database
blackList.Add("test", "test"); // this is where new items are added to the blacklist from the database
DiscordMessage message = await channel.GetMessageAsync(messageID);
await message.ModifyAsync(queue["test"]); // this is where the queue is sent to the Discord channel
}
private async Task Discord_ButtonIsPressed(DiscordClient sender, DSharpPlus.EventArgs.ComponentInteractionCreateEventArgs args)
{
if (args.Interaction.Data.CustomId == "queue")
{
DiscordMessageBuilder message = new();
DiscordEmbedBuilder embed = new();
embed.WithTitle("Queue");
embed.AddField("firstUser", queue["test"]);
message.AddEmbed(embed);
await args.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder(message));
}
// there will be logic to handle the fact that a person has made more than 50 requests in 30 seconds
blackList.Add("test", args.User.Id.ToString());
}
实现
SemaphoreSlim
将确保一次只有一个线程可以执行UpdateQueue
和Discord_ButtonIsPressed
内的临界区。
// Semaphore with 1 initial permit & max 1 permit
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(0, 1);
private async void button10_Click(object sender, EventArgs e)
{
new Thread(async () =>
{
while (true)
{
await semaphoreSlim.WaitAsync(); // wait for permit
try
{
await UpdateQueue();
}
finally
{
semaphoreSlim.Release(); // release
}
await Task.Delay(30000);
}
}).Start();
channel = await discord.GetChannelAsync(queueChannelID);
discord.ComponentInteractionCreated += Discord_ButtonIsPressed;
await discord.ConnectAsync();
await Task.Delay(-1);
}
private async Task UpdateQueue()
{
queue.Add("test", "test");
blackList.Add("test", "test");
DiscordMessage message = await channel.GetMessageAsync(messageID);
await message.ModifyAsync(queue["test"]);
}
private async Task Discord_ButtonIsPressed(DiscordClient sender, DSharpPlus.EventArgs.ComponentInteractionCreateEventArgs args)
{
await semaphoreSlim.WaitAsync(); // wait for a permit
try
{
if (args.Interaction.Data.CustomId == "queue")
{
DiscordMessageBuilder message = new();
DiscordEmbedBuilder embed = new();
embed.WithTitle("Queue");
embed.AddField("firstUser", queue["test"]);
message.AddEmbed(embed);
await args.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder(message));
}
blackList.Add("test", args.User.Id.ToString());
}
finally
{
semaphoreSlim.Release(); // release permit
}
}