C#与C++交互开发系列(十六):使用多线程

在这里插入图片描述

前言

在开发需要高性能的应用程序时,多线程是提升处理效率和响应速度的关键技术。C++ 和 C# 各自拥有不同的线程模型和并发工具。在跨语言开发中,如何有效地利用两者的并发特性,同时确保线程安全和数据一致性,是一个值得探讨的问题。

本文将介绍几种在 C# 与 C++ 跨语言调用场景下处理多线程的常见方式,包括基本线程创建与管理、线程同步、线程池的使用以及多线程数据共享的注意事项。

1. 基础线程创建与管理

在 C++ 和 C# 中,创建线程的方式略有不同。C++ 可以使用 std::thread,而 C# 使用 System.Threading.Thread 类或 Task 类。通过 P/Invoke 机制,我们可以在 C# 中调用 C++ 的线程函数。

C++ 中的线程创建

C++ 标准库提供了 std::thread 来创建和管理线程。下方示例展示了一个简单的线程创建方法,线程执行一个模拟的计算任务。

// CppLibrary.cpp
#include <thread>
#include <iostream>

extern "C" __declspec(dllexport)
void StartCppThread() {
    
    
    std::thread cppThread([]() {
    
    
        for (int i = 0; i < 5; ++i) {
    
    
            std::cout << "Cpp Thread: Iteration " << i ;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::thread::id this_id = std::this_thread::get_id();
            std::cout << " C++ 当前线程 ID: " << this_id << std::endl;// 输出线程 ID
        }
        });
    cppThread.detach();
}

C# 中调用 C++ 的线程函数

C# 使用 DllImport 调用 C++ 中的 StartCppThread 函数,C# 本身的主线程可以继续运行,而 C++ 线程在后台执行。

using System;
using System.Runtime.InteropServices;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void StartCppThread();

    static void Main()
    {
    
    
        Console.WriteLine("Starting C++ thread...");
        StartCppThread();
        Console.WriteLine($"C# main thread continues...当前线程 ID: {
      
      Thread.CurrentThread.ManagedThreadId}");

        Console.ReadLine();  // 保持应用运行,观察 C++ 线程的输出
    }
}

执行结果

Starting C++ thread...
C# main thread continues...当前线程 ID: 1
Cpp Thread: Iteration 0 C++ 当前线程 ID: 27388
Cpp Thread: Iteration 1 C++ 当前线程 ID: 27388
Cpp Thread: Iteration 2 C++ 当前线程 ID: 27388
Cpp Thread: Iteration 3 C++ 当前线程 ID: 27388
Cpp Thread: Iteration 4 C++ 当前线程 ID: 27388

2. 线程同步与互斥

当多个线程共享资源时,必须确保线程安全。C++ 中常用 std::mutex,而 C# 使用 lock 关键字或 Mutex 类。

C++ 中的互斥锁

使用 std::mutex 锁定共享资源,确保只有一个线程可以同时访问它。

#include <mutex>
#include <iostream>

std::mutex mtx;

extern "C" __declspec(dllexport)
void SafeIncrementLock(int* sharedCounter) {
    
    
    std::lock_guard<std::mutex> lock(mtx);
    (*sharedCounter)++;
    std::cout << "Counter incremented to " << *sharedCounter ;
    std::thread::id this_id = std::this_thread::get_id();
    std::cout << " C++ 当前线程 ID: " << this_id << std::endl;// 输出线程 ID
}

C# 调用线程安全的 C++ 函数

在 C# 中,定义一个共享计数器,并通过调用 SafeIncrement 观察线程同步效果。

using System;
using System.Runtime.InteropServices;
using System.Threading;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void SafeIncrementLock(ref int sharedCounter);

    static void Main()
    {
    
    
        int counter = 0;

        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.Length; i++)
        {
    
    
            threads[i] = new Thread(() =>
{
    
    
    SafeIncrementLock(ref counter);
    Console.WriteLine($"C# 当前线程 ID: {
      
      Thread.CurrentThread.ManagedThreadId}");
});
            threads[i].Start();
        }

        foreach (var thread in threads)
        {
    
    
            thread.Join();
        }

        Console.WriteLine($"Final counter value: {
      
      counter}");
    }
}

执行结果

Counter incremented to 1 C++ 当前线程 ID: 36572
Counter incremented to 2 C++ 当前线程 ID: 19000
Counter incremented to 3 C++ 当前线程 ID: 10668
Counter incremented to 4 C++ 当前线程 ID: 11096
Counter incremented to 5 C++ 当前线程 ID: 8016
C# 当前线程 ID: 15
C# 当前线程 ID: 12
C# 当前线程 ID: 14
C# 当前线程 ID: 13
C# 当前线程 ID: 16
Final counter value: 5

3. 使用线程池提高性能

C++11 提供了 std::async,而 C# 中有 TaskThreadPool,两者都能帮助减少线程管理的复杂性,并提高性能。

C++ 中的 std::async

std::async 创建一个后台任务并返回一个 std::future,可以异步执行任务并在需要时获取结果。

#include <future>
#include <iostream>

extern "C" __declspec(dllexport)
int LongRunningTask() {
    
    
    std::thread::id this_id = std::this_thread::get_id();
    std::cout << "C++ 当前线程 ID: " << this_id << std::endl;// 输出线程 ID
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

extern "C" __declspec(dllexport)
int RunAsyncTask() {
    
    
    std::future<int> result = std::async(std::launch::async, LongRunningTask);
    return result.get();  // 等待任务完成并返回结果
}

C# 中的异步调用

C# 中可以使用 Task.Run 并结合 await 进行异步操作。

using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int RunAsyncTask();

    static async Task Main()
{
    
    
    Console.WriteLine("Starting long-running task...");
    int result = await Task.Run(() =>
    {
    
    
        Console.WriteLine($"C# 当前线程 ID: {
      
      Thread.CurrentThread.ManagedThreadId}");
        return RunAsyncTask();
    });
    Console.WriteLine($"Task completed with result: {
      
      result}");
}
}

执行结果

Starting long-running task...
C# 当前线程 ID: 11
C++ 当前线程 ID: 36016
Task completed with result: 42

4. 线程间的数据共享与传递

在跨语言调用中,若多个线程间需要共享数据,需要特别注意数据的一致性和生命周期管理。可以使用共享内存或传递数据的方式来实现。

使用共享内存

C++ 和 C# 都可以使用共享内存进行数据共享。以下示例使用全局变量模拟共享数据的传递。

// CppLibrary.cpp
#include <atomic>

std::atomic<int> sharedData(0);

extern "C" __declspec(dllexport)
void SetSharedData(int value) {
    
    
    std::thread::id this_id = std::this_thread::get_id();
    std::cout << "C++ 当前线程 ID: " << this_id << std::endl;// 输出线程 ID
    sharedData.store(value);
}

extern "C" __declspec(dllexport)
int GetSharedData() {
    
    
    std::thread::id this_id = std::this_thread::get_id();
    std::cout << "C++ 当前线程 ID: " << this_id << std::endl;// 输出线程 ID
    return sharedData.load();
}

在 C# 中,可以使用 SetSharedDataGetSharedData 方法实现数据的读写共享。

using System;
using System.Runtime.InteropServices;

class Program
{
    
    
    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void SetSharedData(int value);

    [DllImport("MyNativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int GetSharedData();

    static void Main()
    {
    
    
        SetSharedData(10);
        int data = GetSharedData();
        Console.WriteLine($"Shared data: {
      
      data}");
    }
}

执行结果

C++ 当前线程 ID: 18752
C++ 当前线程 ID: 18752
C# 当前线程 ID: 1
Shared data: 10

5. 注意事项与最佳实践

  • 线程安全:使用互斥锁、原子变量或其他同步机制确保多线程共享数据时的安全性。
  • 避免阻塞主线程:在 C# 中,可使用 Taskasync/await 异步编程,避免阻塞 UI 或主线程。
  • 跨语言生命周期管理:注意管理对象的生命周期,特别是在 C++ 中动态分配的内存需要在适当的时间释放。
  • 性能优化:合理使用线程池,避免频繁创建和销毁线程以节省资源。

总结

本文介绍了在 C++ 与 C# 交互开发中多线程与并发编程的基本概念,包括如何创建和管理线程、实现线程同步与数据共享,并利用异步和线程池提高程序性能。在跨语言开发时,合理地利用两种语言的并发工具,并确保线程安全和数据一致性,是构建高效稳定的多线程程序的关键。

猜你喜欢

转载自blog.csdn.net/houbincarson/article/details/143276461