如何在 C 中创建一对一的多线程关系和处理异常#

How to create one-to-one multithreading relationship and handling exception in C#

提问人:Gabriele Troviso 提问时间:11/17/2023 最后编辑:EnigmativityGabriele Troviso 更新时间:11/21/2023 访问量:69

问:

这是我第一次在多线程应用程序上工作(一般来说,不仅仅是 C#),因此我只知道关于它的简单概念。我已经实现了基本上使用命名空间实现。System.Threading

我有一个字典,它将对象 A 与其 ID () 链接起来。每个 A 都必须执行一个操作(每个 A 都执行相同的操作),我希望此操作在单独的线程列表中工作,每个 A 一个。Dictionary<int,A>

我知道这是实现多线程的糟糕方法,但我目前只知道这个命名空间。我做了一些事情:System.Threading


public class ThreadManager
{
    private Dictionary<int, A> _aList;
    public Dictionary<int, A> AList
    {
        get { return _aList; }
        set { _aList = value; }
    }

    private Dictionary<int, Thread> _threadList;
    public Dictionary<int, Thread> ThreadList
    {
        get { return _threadList; }
        set { _threadList = value; }
    }

    public ThreadManager(Dictionary<int, A> list)
    {
        AList = list;
        ThreadList = new Dictionary<int, Thread>();

        foreach (A a in AList)
        {
            int id = a.Id;
            ThreadList.Add(id, new Thread(new ParameterizedThreadStart(Operation)));
        }
    }

    public void Operation(object data)
    {
        try
        {
            /*** do something***/
        }
        catch (Exception e)
        {
            /*** throw exception ***/
        }
    }
}

当我在应用程序代码的外部运行特定线程时:

ThreadManager.ThreadList[i].Start(data)
 

做必要的操作似乎工作正常。但是,我认为这个解决方案真的很丑陋,而且功能不是很强大。

此外,还有另一个大问题。当 throw 中出现异常时,正如预期的那样,它不会从线程中捕获。我可以在代码的另一个点中捕获线程异常,这些异常适用于不同的线程(例如主线程)吗?

编辑:

为了清楚起见,我希望没有线程阻塞主线程。我需要能够在操作工作时对主线程进行操作。

C# 多线程 winforms 异常 thread-exceptions

评论

0赞 Julian 11/17/2023
似乎您正在处理数据并行性,因此您可以考虑使用任务并行库。关于你的最后一个问题,确保每个帖子只问一个问题。Parallel.ForEach()
2赞 shingo 11/17/2023
foreach (A a in AList)<-- 这无法编译。
0赞 Gabriele Troviso 11/17/2023
@Julian 我不知道TPL。我会读到的,谢谢。关于最后一项,也许我打开了另一个问题。对不起
2赞 Julian 11/17/2023
然后你应该考虑使用任务,例如实现任务异步模式(TAP)或一直使用异步-await模式,并同时启动多个任务和。请注意,并行性、多线程和异步性不是一回事。await Task.WhenAll(listOfTasks)
1赞 Gabriele Troviso 11/17/2023
@TheodorZoulias是的

答:

0赞 Theodor Zoulias 11/17/2023 #1

我建议它使用 async/await 的组合,并且:Task.RunParallel.ForEach

private async void button1_Click(object sender, EventArgs e)
{
    try
    {
        Cursor = Cursors.WaitCursor;
        button1.Enabled = false;

        await Task.Run(() =>
        {
            ParallelOptions options = new()
            {
                MaxDegreeOfParallelism = Environment.ProcessorCount
            };

            Parallel.ForEach(dictionary, options, entry =>
            {
                var (key, value) = entry;
                /* do something with the key and value */
            });
        });
    }
    catch (AggregateException aex)
    {
        /* handle aex.InnerExceptions (can be more than one) */
    }
    finally
    {
        button1.Enabled = true;
        Cursor = Cursors.Default;
    }
}
  1. 使用 asyncawait 关键字可以编写不会阻塞 UI 线程的异步代码,就像编写普通同步代码一样。当异步操作在进行中时,UI 保持响应。
  2. Task.Run 将委托的处理卸载到 ThreadPool 线程。是由 .NET 基础结构管理的可重用线程池。如有必要,可以使用 ThreadPool.SetMinThreads 方法对其进行微调。actionThreadPool
  3. Parallel.ForEach 在多个线程上并行调用委托,从而强制执行 MaxDegreeOfParallelism 策略。如果您想要无限的并行性,请使用 .bodyThreadPoolMaxDegreeOfParallelism = -1

如果发生异常,将停止调用委托,并在所有当前正在运行的调用完成后完成。所有错误调用的错误都将捆绑在 AggregateException 中。此异常将首先由 传播,然后由运算符传播,您将能够在 UI 线程上使用事件处理程序中的 / 块处理它。您也可以让它不处理(省略块),在这种情况下,它将作为常规 Application.ThreadException 事件处理程序进行处理。如果也省略了这一点,则 WinForms 应用程序默认显示的默认错误弹出窗口将通知用户,并为用户提供继续或退出应用程序的选项。Parallel.ForEachbodyTask.RunawaittrycatchClickcatch

评论

0赞 Gabriele Troviso 11/20/2023
为了实现我的部分目标,这个解决方案非常好。此外,我还注意到,当我停止所有带有全局变量的线程时,它比我的实现更有效。但是,我没有找到一种处理具有这种结构的单个线程的方法。我尝试过像布尔列表(或字典)之类的东西,但不起作用。这是您实施的限制吗?
0赞 Gabriele Troviso 11/20/2023
编辑:我找到了一个机械解决方案来删除特定线程:停止所有带有全局变量的线程,删除字典中的线程 id,然后再次运行所有线程。为了工作,我使用全局变量在循环时进行检查。你觉得怎么样?有没有更好的方法?
0赞 Theodor Zoulias 11/21/2023
@GabrieleTroviso “但是,我没有找到一种处理具有这种结构的单个线程的方法。” -- 你能更详细地描述一下你的意思吗?它与例外有关吗?什么是理想的行为,你观察到什么行为?
0赞 Gabriele Troviso 11/21/2023
我没有谈论异常,而是谈论一般的线程。我想在需要时停止或运行单个线程,以及停止或运行所有线程。这就是我最初选择字典的原因
1赞 Theodor Zoulias 11/21/2023
@GabrieleTroviso查看我在另一个关于动态并行度的问题中发布的这个答案。该属性有一个 setter,用于将其限制为正值,但您可以将 to 更改为允许值为零,并有效地暂停并行执行。我认为它应该适用于您的情况。DynamicParallelOptions.DegreeOfParallelismif (value < 1)if (value < 0)