写入位置冲突,线程之间共享变量

时间:2020-07-30 19:54:34

标签: c# selenium unity3d selenium-chromedriver

我正在构建一个使用Selenium Webdriver的Unity应用程序,我在另一个线程中启动了无头浏览器(chromedriver)。在该线程中,我继续拍摄无头浏览器中显示的屏幕快照,并将其存储在字节数组中。 在主线程中,我必须使用该字节数组来制作Unity可以理解的图像(Texture2D)。 我启动了该应用程序,它工作了一段时间,但是过了一段时间,应用程序崩溃了。崩溃文件说:

UnityPlayer.dll在模块中导致访问冲突(0xc0000005) 0023:105311be的UnityPlayer.dll。

错误发生在2020-07-30_150418。 C:\ Codes \ Apps \ MyApp \ MyApp.exe,由Fabio运行。

正在使用94%的内存。

0 MB物理内存[423 MB可用空间]。

0 MB分页文件[1990 MB可用]。

0 MB用户地址空间[108 MB可用空间]。

写入位置007e7200导致访问冲突。

应用程序启动后立即启动第二个线程和方法“ LoadImage”(作为Coroutine)

获取屏幕截图的代码:

public async Task ScreenShotBrowser()
    {
        //await Task.Yield();
        while (isBrowserOpen)
        {
                Screenshot screenshot = ssdriver.GetScreenshot();  //Selenium stuff
            lock (imgArray)
            {
                imgArray = screenshot.AsByteArray;               
            }
            Thread.Sleep(100);
        }
        
    }

将图像加载到统一材质中

IEnumerator LoadImage()
    {
        while (!isBrowserOpen) { yield return new WaitForSecondsRealtime(0.1f); } // ugly code, but avoid freezing

        while (isBrowserOpen)
        {
            
            lock (imgArray)
            {
                mat.mainTexture = loadTexture(imgArray);
            }
            yield return new WaitForSecondsRealtime(0.1f); 
        }
    }

    private Texture2D loadTexture(byte[] imageArray)
    {
            Texture2D tex = new Texture2D(1, 1);
        lock (imageArray)
        {
            tex.LoadImage(imageArray);
        }
        return tex;
    }

预先感谢

1 个答案:

答案 0 :(得分:1)

不确定这是否是问题所在,但我认为这里确实发生了内存不足的情况。

private Texture2D loadTexture(byte[] imageArray)
{
    Texture2D tex = new Texture2D(1, 1);
    lock (imageArray)
    {
        tex.LoadImage(imageArray);
    }
    return tex;
}

这会经常在您的应用程序中创建并加载新的Texture2D。但是,即使不再引用纹理,也不会自动收集GC!您必须积极销毁它。

所以我要么做类似的事情

IEnumerator LoadImage()
{
    // You could also use this
    yield return new WaitWhile(()=>!isBrowserOpen);

    while (isBrowserOpen)
    {
        // destroy the current texture first
        if(mat.mainTexture) Destroy(mat.mainTexture);

        lock (imgArray)
        {
            mat.mainTexture = loadTexture(imgArray);
        }

        yield return new WaitForSecondsRealtime(0.1f); 
    }
}

或者不是每次都创建新的纹理,而是简单地

private Texture2D loadTexture(byte[] imageArray, Texture2D tex)
{
    lock (imageArray)
    { 
        // Load into and overwrite the existing texture
        tex.LoadImage(imageArray);
    }
}

IEnumerator LoadImage()
{
    // You could also use this
    yield return new WaitWhile(()=>!isBrowserOpen);

    // Create the texture once if it doesn't exist yet
    if(!mat.mainTexture) mat.mainTexture = new Texture2D(1f, 1f);

    while (isBrowserOpen)
    {
        lock (imgArray)
        {
            loadTexture(imgArray, mat.mainTexture);
        }

        yield return new WaitForSecondsRealtime(0.1f); 
    }
}

除了注释中提到的内容外,lock imgArray可能是一个问题,但是稍后在imgArray = screenshot.AsByteArray;中交换该引用。在这里我不确定,但是只是为了保存起见,您应该遵循将专用对象用于锁之类的建议,例如

///<summary>
/// This is the Lock object for <see cref="imgArray" />
/// </summary>
private readonly object imgArrayLock = new object();

private Texture2D loadTexture(byte[] imageArray, Texture2D tex)
{
    lock (imgArrayLock)
    { 
        // Load into and overwrite the existing texture
        tex.LoadImage(imageArray);
    }
}

IEnumerator LoadImage()
{
    // You could also use this
    yield return new WaitWhile(()=>!isBrowserOpen);

    // Create the texture once if it doesn't exist yet
    if(!mat.mainTexture) mat.mainTexture = new Texture2D(1f, 1f);

    while (isBrowserOpen)
    {
        lock (imgArrayLock)
        {
            loadTexture(imgArray, mat.mainTexture);
        }

        yield return new WaitForSecondsRealtime(0.1f); 
    }
}