C#中线程同步的问题

问题描述 投票:0回答:1

我正在用 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());
}
c# multithreading asynchronous race-condition
1个回答
0
投票

实现

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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.