繁体   English   中英

为什么这个无辜的 function 会导致段错误?

[英]Why does this innocent function cause a segfault?

我正在尝试用 C++/SFML 编写元球,我的程序在单个线程中运行良好。 我试图写一个 MRE 来找到问题,这就是我得到的:

主文件

// main

#include <iostream>
#include "threader.h"

float func(float x, float y, float a, float b, float r) {
    return 1.f / sqrt((x - a)*(x - a) + (y - b)*(x - b));
}

int main () {

    sf::RenderWindow window(sf::VideoMode(2800, 1800), "");
    window.setFramerateLimit(20);
    sf::Event event{};
    threader tf(window);
    while (window.isOpen()) {
        while (window.pollEvent(event)) {
            switch (event.type) {
            case sf::Event::Closed: {
                window.close();
            }
            }
        }
        window.clear();

        tf.parallel(func);

        window.display();
    }
}

线程器.h

//threader_H

#pragma once

#include <array>
#include <thread>
#include <cmath>
#include <functional>
#include <SFML/Graphics.hpp>

class threader {
public:
    int threadCount = 4;
    std::array<std::thread, 4> threads;
    sf::RenderWindow& window;
public:
    explicit threader(sf::RenderWindow& w) : window(w) {};

    void strip(int start, int end, const std::function<float(float, float, float, float, float)>& func);
    void parallel(const std::function<float(float, float, float, float, float)>& func);

};

线程器.cpp

// threader_CPP

#include "threader.h"
#include <iostream>


void threader::strip (int start, int end, const std::function<float (float, float, float, float, float)> &func) {
    for (int X = start; X < end; X += 10) {
        for (int Y = 0; Y < window.getSize().y; Y += 10) {
            auto x = static_cast<float>(X);
            auto y = static_cast<float>(Y);

            x = x / 2800.f * 4 + 0 - 4 / 2.f;
            y = -((1800.f - y) / 1800 * 4 + 0 - 4 / 2.f);

            if (func(x, y, 1, 2, 3) > 1) {
                sf::CircleShape circle(20);
                circle.setPointCount(3);
                circle.setFillColor(sf::Color::Cyan);
                circle.setPosition(X, Y);
                window.draw(circle);
            }
        }
    }
}

void threader::parallel (const std::function<float (float, float, float, float, float)> &func) {
    int start = 0;
    int end = window.getSize().x / threadCount;
    for (auto& t : threads) {
        t = std::thread(&threader::strip, this, start, end, func);
        start = end;
        end += window.getSize().x / threadCount;
    }
    for (auto& t : threads) {
        t.join();
    }

}

现在解释一下。 threader is a class which has two methods: strip that calculates a function for a given strip of the window and parallel that creates threads to separately calculate my function for every strip. 此代码不起作用:

任何 C++ 程序的成功结果都是段错误:)

但这里有个问题:如果我在 main 中调整 function void func(...)以返回1.f / sqrt((x - a) + (y - b)) ,一切正常。 怎么了? 一个简单的计算如何导致段错误? 请帮忙...

编辑 1:用 CLion 编写,C++ 20。

编辑2:如果这里有任何意义,请向我解释。 调试器

与其尝试在多个线程上绘制到 window,不如使用std算法过滤点以绘制圆,并将它们全部绘制在主线程上。

std::vector<std::pair<int, int>> getPoints(const sf::RenderWindow& window) {
    std::vector<std::pair<int, int>> points;
    for (int X = 0; X < window.getSize().x; X += 10) {
        for (int Y = 0; Y < window.getSize().y; Y += 10) {
            points.emplace_back(X, Y);
        }
    }
    return points;
}

template<typename F>
auto filter(F f) {
    return [f](const std::pair<int, int> & point) {
        auto x = static_cast<float>(point.first);
        auto y = static_cast<float>(point.second);

        x = x / 2800.f * 4 + 0 - 4 / 2.f;
        y = -((1800.f - y) / 1800 * 4 + 0 - 4 / 2.f);

        return (func(x, y, 1, 2, 3) > 1);
    }
}

sf::CircleShape toCircle(int X, int Y) {
    sf::CircleShape circle(20);
    circle.setPointCount(3);
    circle.setFillColor(sf::Color::Cyan);
    circle.setPosition(X, Y);
    return circle;
}

template <typename F>
void drawCircles(sf::RenderWindow& window, F f) {
    auto points = getPoints(window);
    auto end = std::remove_if(std::execution::par, points.begin(), points.end(), filter(f));
    points.erase(end, points.end());
    for (auto & [X, Y] : points) {
        window.draw(toCircle(X, Y));
    }
}

float func(float x, float y, float a, float b, float r) {
    return 1.f / sqrt((x - a)*(x - a) + (y - b)*(x - b));
}

int main () {

    sf::RenderWindow window(sf::VideoMode(2800, 1800), "");
    window.setFramerateLimit(20);
    sf::Event event{};
    threader tf(window);
    while (window.isOpen()) {
        while (window.pollEvent(event)) {
            switch (event.type) {
            case sf::Event::Closed: {
                window.close();
            }
            }
        }
        window.clear();

        drawCircles(window, func);

        window.display();
    }
}

在不正确切换 GL 上下文的情况下尝试从多个线程进行渲染会出现问题。 由于您是在 CPU 上执行此操作,因此这里有一些选项:

  1. 制作多个 sf::RenderTexture 对象,在单独的线程中渲染到每个对象(不要忘记在最后调用 display() ),然后在一些 sprite 中将它们绘制在主线程上。 (比较难,不确定是不是go的路,但不知道你到底想做什么)

  2. 制作多个 sf::Image 对象,在单独的线程中渲染它们中的每一个,然后在一些精灵中将它们绘制在主线程上。

  3. 只制作一个 sf::Image object,每个线程在图像的一个单独区域中渲染,然后在某个 sprite 中在主线程上绘制图像。

选项 2 和 3 使其在像素级别上更容易使用,也更快。 确保以缓存友好的方式遍历图像(最有可能逐行)

我会 go 与 2 或 3 取决于您认为它对您的应用程序更好。

此外,使用 sf::Transform 从屏幕空间(图像空间)转换到元球所在的世界空间。

https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Image.php

https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Transform.php#ab42a0bb7a252c6d221004f6372ce5fdc

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM