我有一個VS2015解決方案,包含非託管C++代碼(用於執行一些CPU密集型仿真計算),圍繞此代碼的C++/cli包裝以及通過DLL調用C++/cli包裝的ac#項目。以下示例是完整代碼的簡化版本,對於代碼數量提前感到抱歉,但需要全面瞭解正在進行的操作。避免std :: deque迭代器在非託管C++,C++/cli和c#之間的不可忽略錯誤代碼
不受管理的C++代碼
class diffusion_limited_aggregate {
public:
diffusion_limited_aggregate()
: aggregate_map(), attractor_set(), batch_queue() {}
std::size_t size() const noexcept { return aggregate_map.size(); }
std::queue<std::pair<int,int>>& batch_queue_handle() noexcept { return batch_queue; }
void generate(std::size_t n) {
initialise_attractor_structure(); // set up initial attractor seed points
std::size_t count = 0U;
std::pair<int,int> current = std::make_pair(0,0);
std::pair<int,int> prev = current;
bool has_next_spawned = false;
while (size() < n) {
if (!has_next_spawned) {
// => call function to spawn particle setting current
has_next_spawned = true;
}
prev = current;
// => call function to update random walking particle position
// => call function to check for lattice boundary collision
if (aggregate_collision(current, prev, count)) has_next_spawned = false;
}
}
void initialise_attractor_structure() {
attractor_set.clear();
attractor_set.insert(std::make_pair(0,0));
}
void push_particle(const std::pair<int,int>& p, std::size_t count) {
aggregate_map.insert(std::make_pair(p, count));
batch_queue.push(p);
}
bool aggregate_collision(const std::pair<int,int>& current,
const std::pair<int,int>& prev, std::size_t& count) {
if (aggregate_map.find(current) != aggregate_map.end()
|| attractor_set.find(current) != attractor_set.end()) {
push_particle(previous, ++count);
return true;
}
return false;
}
private:
std::unordered_map<std::pair<int,int>,
std::size_t,
utl::tuple_hash> aggregate_map;
std::unordered_set<std::pair<int,int>, utl::tuple_hash> attractor_set;
std::queue<std::pair<int,int>> batch_queue; // holds buffer of aggregate points
};
哪裏utl::tuple_hash
爲std::pair
散列函數對象,並且更一般地,std::tuple
情況下,定義爲:
namespace utl {
template<class Tuple, std::size_t N>
struct tuple_hash_t {
static std::size_t tuple_hash_compute(const Tuple& t) {
using type = typename std::tuple_element<N-1, Tuple>::type;
return tuple_hash_t<Tuple,N-1>::tuple_hash_compute(t)
+ std::hash<type>()(std::get<N-1>(t));
}
};
// base
template<class Tuple>
struct tuple_hash_t<Tuple, 1> {
static std::size_t tuple_hash_compute(const Tuple& t) {
using type = typename std::tuple_element<0,Tuple>::type;
return 51U + std::hash<type>()(std::get<0>(t))*51U;
}
};
struct tuple_hash {
template<class... Args>
std::size_t operator()(const std::tuple<Args...>& t) const {
return tuple_hash_t<std::tuple<Args...>,sizeof...(Args)>::tuple_hash_compute(t);
}
template<class Ty1, class Ty2>
std::size_t operator()(const std::pair<Ty1, Ty2>& p) const {
return tuple_hash_t<std::pair<Ty1,Ty2>,2>::tuple_hash_compute(p);
}
};
}
託管C++/CLI包裝器
以下是圍繞類diffusion_limited_aggregate
的C++/cli中的一個包裝,這種情況下的重要方法是ProcessBatchQueue
。此方法是std::deque iterator not dereferencable error
必須發生的地方,因爲它是訪問和彈出batch_queue
內容的唯一位置。
public ref class ManagedDLA2DContainer {
private:
diffusion_limited_aggregate* native_dla_2d_ptr;
System::Object^ lock_obj = gcnew System::Object();
public:
ManagedDLA2DContainer() : native_dla_2d_ptr(new diffusion_limited_aggregate()) {}
~ManagedDLA2DContainer() { delete native_dla_2d_ptr; }
std::size_t Size() { return native_dla_2d_ptr->size(); }
void Generate(std::size_t n) { native_dla_2d_ptr->generate(n); }
System::Collections::Concurrent::BlockingCollection<
System::Collections::Generic::KeyValuePair<int,int>
>^ ProcessBatchQueue() {
// store particles in blocking queue configuration
System::Collections::Concurrent::BlockingCollection<
System::Collections::Generic::KeyValuePair<int,int>>^ blocking_queue =
gcnew System::Collections::Concurrent::BlockingCollection<
System::Collections::Generic::KeyValuePair<int,int>
>();
System::Threading::Monitor::Enter(lock_obj); // define critical section start
try {
// get ref to batch_queue
std::queue<std::pair<int,int>>& bq_ref = native_dla_2d_ptr->batch_queue_handle();
// loop over bq transferring particles to blocking_queue
while (!bq_ref.empty()) {
auto front = std::move(bq_ref.front());
blocking_queue->Add(System::Collections::Generic::KeyValuePair<int,int>(front.first,front.second));
bq_ref.pop();
}
}
finally { System::Threading::Monitor::Exit(lock_obj); }
return blocking_queue;
}
}
C#代碼
最後,我有一個使用ManagedDLA2DContainer
以產生聚集體和一個接口上顯示它們下面的C#代碼。
public partial class MainWindow : Window {
private static readonly System.object locker = new object();
private readonly ManagedDLA2DContainer dla_2d;
public MainWindow() {
InitializeComponent();
dla_2d = new ManagedDLA2DContainer();
}
private void GenerateAggregate(uint n) {
// start asynchronous task to perform aggregate simulation computations
Task.Run(() => CallNativeCppAggregateGenerators(n));
System.Threading.Thread.Sleep(5);
// start asynchronous task to perform rendering
Task.Run(() => AggregateUpdateListener(n));
}
private void CallNativeCppAggregateGenerators(uint n) {
dla_2d.Generate(n);
}
private void AggregateUpdateListener(uint n) {
const double interval = 10.0;
Timer timer = new Timer(interval);
timer.Elapsed += Update2DAggregateOnTimedEvent;
timer.AutoReset = true;
timer.Enabled = true;
}
private void Update2DAggregateOnTimedEvent(object source, ElapsedEventArgs e) {
lock(locker) {
BlockingCollection<KeyValuePair<int,int>> bq = dla_2d.ProcessBatchQueue();
while(bq.Count != 0) {
KeyValuePair<int,int> p = bq.Take();
Point3D pos = new Point3D(p.Key, p.Value, 0.0);
// => do stuff with pos, sending to another class method for rendering
// using Dispatcher.Invoke(() => { ... }); to render in GUI
}
}
}
}
方法GenerateAggregate
僅稱爲每個聚集體執行一次,它被通過一個按鈕的處理程序方法稱爲我有它調用一個GenerateAggreate
OnGenerateButtonClicked
事件處理函數的界面上形成Generate
方法。代碼中的其他任何地方都不會調用CallNativeCppAggregateGenerators
和AggregateUpdateListener
。
的問題
如託管的包裝部分,執行該代碼時,我偶爾會在運行時斷言錯誤提到,
std::deque
迭代器不dereferencable。
這往往會在首次執行時發生,但它也會出現在正在進行的總計生成過程中,因此生成聚合的啓動代碼很可能不是此處的罪魁禍首。
我該如何着手解決這個問題?希望這是在我的關鍵部分代碼或類似的一些邏輯錯誤的簡單情況下,但我還沒有找到確切的問題。
正如在評論中指出,該問題可能是元素正在不斷增加batch_queue
而C#線程調用ProcessBatchQueue
在消費隊列中的元素,從而可能無效batch_queue
的迭代器。是否有典型的生產者 - 消費者設計模式可以應用於這種用例?
編輯:這將是很好,如果downvoter可以給自己的理由,這樣我可以改善這個問題。
在嘗試使用迭代器之前,您可以添加一個檢查以查看容器是否爲空?這可以幫助縮小問題的來源。 – NathanOliver
@NathanOliver我想我可以,但我假設,出現此錯誤是由於張女士不知何故'batch_queue'變空,而裏面的',而(!bq_ref.empty())'ProcessBatchQueue'的'循環,只要因爲它不是空的,那麼我認爲(也許我錯了)訪問'front'和'pop'隊列將是明確的操作。 – ArchbishopOfBanterbury
您沒有顯示從何處調用GenerateAggregate。 CallNativeCppAggregateGenerators是否從其他地方撥打電話?如果在處理現有隊列時向隊列添加新元素,則在處理過程中使用的迭代器將失效。 – 1201ProgramAlarm