Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/148.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 调试另一个线程更改我的数据的实例_C++_C_Visual Studio 2008_Multithreading_Debugging - Fatal编程技术网

C++ 调试另一个线程更改我的数据的实例

C++ 调试另一个线程更改我的数据的实例,c++,c,visual-studio-2008,multithreading,debugging,C++,C,Visual Studio 2008,Multithreading,Debugging,我有一个庞大的全球结构阵列。阵列的某些区域绑定到单个线程,这些线程可以修改其阵列区域,而无需使用关键部分。但数组中有一个特殊区域,所有线程都可以访问该区域。访问数组这些部分的代码需要小心地使用关键部分(每个数组元素都有自己的关键部分),以防止两个线程同时写入结构 现在我有一个神秘的错误,我正试图追逐,它是发生不可预测的,非常罕见。似乎其中一个结构中填充了一些错误的数字。一个明显的解释是,当另一个线程被排除在外时,它意外地被允许设置这个数字 不幸的是,跟踪这个bug几乎是不可能的。出现错误数据的数

我有一个庞大的全球结构阵列。阵列的某些区域绑定到单个线程,这些线程可以修改其阵列区域,而无需使用关键部分。但数组中有一个特殊区域,所有线程都可以访问该区域。访问数组这些部分的代码需要小心地使用关键部分(每个数组元素都有自己的关键部分),以防止两个线程同时写入结构

现在我有一个神秘的错误,我正试图追逐,它是发生不可预测的,非常罕见。似乎其中一个结构中填充了一些错误的数字。一个明显的解释是,当另一个线程被排除在外时,它意外地被允许设置这个数字


不幸的是,跟踪这个bug几乎是不可能的。出现错误数据的数组元素每次都不同。我希望能够为bug设置某种陷阱,如下所示:我将为数组元素N输入一个关键部分,然后我知道没有其他线程能够接触数据,然后(直到我退出关键部分)为调试工具设置某种标志,表示“如果任何其他线程试图更改此处的数据,请中断并向我显示令人不快的源代码补丁”…但我怀疑不存在此类工具…或者确实存在?或者我应该采用一些完全不同的调试方法。

我知道两种处理此类错误的方法:

1) 反复阅读代码,寻找可能的错误。我可以考虑两个可能导致这种情况的错误:不同步的访问或不正确的内存地址写入。也许你有更多的想法


2) 日志,记录日志。添加大量可选跟踪(OutputDebugString或日志文件),在每个关键位置,包含足够的信息-索引、变量值等。最好使用一些#ifdef添加此跟踪。复制错误并尝试从日志中了解发生了什么。

用透明的静音类包装数据如何?然后可以应用附加的锁状态检查

class critical_section;

template < class T >
class element_wrapper
{
public:
    element_wrapper(const T& v) : val(v) {}
    element_wrapper() {}
    const element_wrapper& operator = (const T& v) {
#ifdef _DEBUG_CONCURRENCY 
        if(!cs->is_locked())
            _CrtDebugBreak();
#endif
        val = v;
        return *this;
    }
    operator T() { return val; }
    critical_section* cs;
private:
    T val;
};
实际上,可以使用
::tryInterCriticalSection
(如果成功,后面跟着
::LeaveCriticalSection
)来确定是否拥有关键部分,而不是自定义
关键部分::locked
标志。尽管如此,上面的实现几乎一样好

因此,适当的用法是:

typedef std::vector< element_wrapper<int> > cont_t;

void change(cont_t::reference x) { x.lock(); x = 1; x.unlock(); }

int main()
{
    cont_t container(10, 0); 

    std::for_each(container.begin(), container.end(), &change);
}
typedef std::vectorcont;
无效更改(cont_t::reference x){x.lock();x=1;x.unlock();}
int main()
{
容器(10,0);
std::for_each(container.begin()、container.end()、&change);
}

您最好(最快)的赌注仍然是修改互斥代码。正如您所说,这是显而易见的解释-为什么不尝试真正找到解释(通过逻辑)而不是额外的提示(通过编码)这可能是不确定的?如果代码检查没有发现有用的东西,您仍然可以使用互斥代码并将其用于测试运行。第一次尝试不应该是在系统中重现错误,而是确保互斥实现线程的正确实现(从2开始)它们都会一次又一次地尝试访问相同的数据结构,每个数据结构都有一个随机的小延迟,以使它们在时间线上抖动。如果此测试导致出现错误的互斥,而您在代码中根本无法识别,那么您就成了某种依赖于体系结构的影响的受害者(可能是指令重新排序、多核缓存不一致等)并需要找到另一个互斥实现。如果您发现互斥中存在明显的错误,请尝试在实际系统中利用它(插入代码以使错误更频繁地出现)因此,您可以确保这确实是您最初问题的原因。

我在骑车上班时考虑过这一点。处理这一问题的一种可能方法是,在没有通过关键部分所有权进行主动访问和保护时,将有问题的内存部分设置为只读。这是假设问题是由于线程不拥有适当的临界段而写入内存

这有很多限制,使它无法工作。最重要的是,我认为您只能逐页设置权限(我相信是4K)。因此,这可能需要对您的分配方案进行一些非常具体的更改,以便您可以缩小要保护的适当部分。第二个问题是,如果另一个线程主动拥有该关键部分,它将不会捕获写入内存的恶意线程。但它会捕获该线程并导致立即访问如果关键部分未被拥有,则启用

您可以将EnterCriticalSection调用更改为:

EnterCriticalSection()
VirtualProtect( … PAGE_READWRITE … );
VirtualProtect( … PAGE_READONLY … );
LeaveCriticalSection()
并将LeaveCriticalSection调用更改为:

EnterCriticalSection()
VirtualProtect( … PAGE_READWRITE … );
VirtualProtect( … PAGE_READONLY … );
LeaveCriticalSection()
下面的代码块显示了对

intmain(intargc,char*argv[]1
{
无符号字符*mem;
int i;
德沃德·德沃尔德;
//这假设页面大小为4K
mem=malloc(4096*10);
对于(i=0;i<10;i++)
mem[i*4096]=i;
//将第二页设置为只读。来自malloc的分配为
//不一定在页面边界上,但这肯定会在
//第二页。
printf(“VirtualProtect res=%d\n”,
虚拟保护(mem+4096,
1,//结束设置整个页面
第页(只读,&dwOld));
//我还能读吗
对于(i=1;i<10;i++)
printf(“%d”,mem[i*4096]);
printf(“\n”);
//除了第二页外,其他所有页面都可以写入
对于(i=0;i<10;i++)
if(i!=1)//避免我们设置为只读的第二页
mem[i]=1;
//这会导致访问冲突
mem[4096]=1;
}