在C ++ / CLI和C#之间同步线程

时间:2016-05-15 17:57:32

标签: c# multithreading synchronization c++-cli

说我有以下C++/CLI课程:

public ref class ManagedDLAContainer {

private:
    DLAContainer* native_dla_container;
public:
    ManagedDLAContainer() : native_dla_container(new DLAContainer()) {}
    ~ManagedDLAContainer() { delete native_dla_container; }

    KeyValuePair<int,int> GetMRAParticle() {
        std::pair<int,int> mra_p = native_dla_container->mra_particle();
        KeyValuePair<int,int>^ mra_kvp = gcnew 
                     KeyValuePair<int,int>(mra_p.first, mra_p.second);
        return *mra_kvp;
    }

    size_t Size() {
        return native_dla_container->size();
    }

    void Generate(size_t _n) {
        native_dla_container->generate(_n);
    }

};

其中DLAContainer是一个非托管的本机C++类。此类的方法generate执行涉及构建粒子系统的计算密集型计算,而mra_particle返回表示最近添加的粒子的std::pair<int,int>DLAContainer。这个C++/CLI代码打包在一个类库中,然后由C# WPF项目使用。

WPF项目包含以下类:

public partial class MainWindow : Window {
     private static readonly object locker = new object();
     private readonly ManagedDLAContainer dla;
     private KeyValuePair<int,int> mra_pair;
     private readonly AggregateSystemManager aggregate_manager;

     public MainWindow() {
         InitializeComponent();
         dla = new ManagedDLAContainer();
         mra_pair = new KeyValuePair<int,int>();
         aggregate_manager = new AggregateSystemManager();
         // a Model3DGroup which is part of the GUI
         WorldModels.Children.Add(aggregate_manager.AggregateSystemModel());
     }

     private void AggregateUpdateListener(uint _particle_slider_val){
         while (dla_2d.Size() < _particle_slider_val) {
             KeyValuePair<int,int> agg_kvp = dla.GetMRAParticle();
             if (agg_kvp.Equals(mra_pair) {
                 // no updates to aggregate
             }
             else {
                 mra_pair = agg_kvp;
                 Point3D position = new Point3D(agg_kvp.Key, agg_kvp.Value,0);
                 aggregate_manager.AddParticle(position);
                 Dispatcher.Invoke(() => { aggregate_manager.Update(); } );
             }
         }
     }

     private void GenerateAggregate() {
         lock(locker) {
             uint particle_slider_val = 0;
             Dispatcher.Invoke(() => {
                 particle_slider_val = (uint)particles_slider.Value;
             });
             // start AggregateUpdateListener in new task
             Task.Factory.StartNew(() => AggregateUpdateListener(particle_slider_val));
             // generate the aggregate
             dla.Generate(particle_slider_val);
         }
     }

     private void GenerateButtonHandler(object sender, RoutedEventArgs e) {
         // start GenerateAggregate method in new task
         Task.Factory.StartNew(() => GenerateAggregate());
     }

}

程序流程说明

  • 用户使用particle_slider GUI元素设置要生成的粒子数,然后单击生成按钮。
  • 方法GenerateAggregate使用Task.Factory.StartNew在新任务中运行,然后此函数在单独的任务中运行AggregateUpdateListener,最后调用Generate以生成粒子系统
  • AggregateUpdateListenerGenerate运行时连续运行并检查最近添加的粒子的更新,并使用{{1}将新粒子渲染到界面必要的类。

问题

虽然此程序大部分都是成功的,但AggregateManager方法偶尔会遗漏使用ManagedDLAContainer::Generate(size_t)生成的粒子,从而导致界面中显示的粒子系统出现间隙。

我认为,这里的问题是两个过程(粒子系统的生成和检查渲染过程)没有以正确同步的方式运行。我需要以某种方式得到它,以便在将粒子添加到系统时触发一个事件,允许AggregateUpdateListener然后执行渲染,然后将控制权交还给代。

我不确定如何做到这一点,因为我的AggrgegateUpdateListener函数将在后台不间断运行,直到粒子系统完全生成到所需的粒子数量为止 - 并执行此过程通过幕后的原生Generate代码,对我的C++项目一无所知。出于这个原因,我认为使用像C#这样的东西在这种情况下是不适用的,但如果是,那么请让我知道如何!

我现在能够提出的唯一解决方案(与正确同步过程无关)是迭代GUI的最终粒子系统并与{{的粒子系统容器进行比较检查1}}代码(总是正确的)并且当通过与后者比较检测到未命中时填充前者中的任何缺失间隙。但这是一个讨厌的解决方案&#34;而且我更愿意让它正确地实时运行。

如果需要更多信息,请与我们联系。

1 个答案:

答案 0 :(得分:1)

您可以尝试在C ++和C#中使用命名的Semaphore,但它可能有点重,因为它意味着进程之间的同步。

否则,按照Hans的评论,您可以在托管C ++部分中创建BlockingCollection并将其公开给C#项目。然后,您需要使用ManagedDLAContainer中的所有粒子并将它们排入阻塞队列。

在C#GUI中,我建议你每200 / 250ms有一个计时器,当它触发时会排队队列中所有可用的粒子,然后更新GUI。确保通过一些最大数量的更新来绑定它,这样就不会不断地将项目从队列中拉出来(如果本机代码比C#代码更快)。