提问人:Lok 提问时间:10/26/2023 最后编辑:Lok 更新时间:10/27/2023 访问量:103
C 语言中的线程同步问题#
Problem with thread synchronisation in C#
问:
我正在用 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());
}
答:
1赞
hakim00
10/27/2023
#1
实现将确保一次只有一个线程可以执行关键部分。SemaphoreSlim
UpdateQueue
Discord_ButtonIsPressed
// Semaphore with 1 initial permit & max 1 permit
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,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
}
}
评论
0赞
Lok
10/27/2023
感谢您的回复!您能解释一下这种实现与锁设计有何不同吗?
0赞
hakim00
10/27/2023
Lock
只能用于同步代码。例如,您将无法在锁内使用。请参阅 learn.microsoft.com/en-us/dotnet/csharp/language-reference/...。await
Discord_ButtonisPressed
0赞
hakim00
10/27/2023
如果需要使用异常,则可能需要添加适当的异常处理。
0赞
Lok
10/27/2023
谢谢。还有,会不会有我写过的问题?but I think then the thread updating the queue would have to wait a long time to be let into the lock block, which means that the queue would not be updated every 30 seconds
0赞
hakim00
10/27/2023
您只需要锁定黑名单的资源还是队列?
评论
Thread
async
async void