【问题标题】:Threads and sockets, threads and objects more generally更一般的线程和套接字,线程和对象
【发布时间】:2020-12-14 18:25:08
【问题描述】:

感谢您的宝贵时间。

我想要完成什么? 我正在尝试利用线程来加速我的程序。经过一些分析后,我发现我的大部分程序时间(图形应用程序)都用于检查我的套接字状态。在尝试减少脂肪并降低到每个周期

有什么问题? 我无法理解线程和对象,我玩了一些运行良好的教科书示例并将本地函数与运行良好的线程相结合。试图将其转移到我自己的代码中已经证明超出了我的能力。

我尝试了什么? 我试图寻找智能指针来在堆上分配我的 UDPSocket 对象,希望所有线程都可以访问堆内存。出于同样的原因,我尝试了旧的新和删除。我尝试将我的 UDPSockets 包装在另一个对象中,并让整个对象在另一个线程上启动。

总结 可以肯定的是,我对线程的理解存在很大的漏洞,我将不胜感激这个特定问题的解决方案,但也可以链接到任何好的文章、教程、视频等这可能有助于加深我的理解。也许我只需要重新检查我的整个 UDPSocket 类?非常欢迎您的建议。

我将在下面发布我的示例,请注意,为了便于阅读,我已经删除了所有错误检查等。

#pragma once
#define WIN32_MEAN_AND_LEAN
#include <WS2tcpip.h>
#include <iostream>
#include <memory>
#include <thread>
#pragma comment(lib, "ws2_32.lib")


class UDPServer
{

public:
    UDPServer(unsigned short port_in)
        :
        port(port_in)
    {
        // Startup Winsock
        WSADATA data;
        WORD version = MAKEWORD(2, 2);
        int wsOk = WSAStartup(version, &data);

        //Bind socket to port, Any Address
        s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        //Hint structure
        sockaddr_in serverHint;
        serverHint.sin_addr.S_un.S_addr = ADDR_ANY;
        serverHint.sin_family = AF_INET;
        serverHint.sin_port = htons(port);

        bind(s, (sockaddr*)&serverHint, sizeof(serverHint));
    
    }
    ~UDPServer()
    {
        closesocket(s);
        WSACleanup();
    }

    bool Recieve()
    {
        ZeroMemory(&client, clientLength);

        if (dataAvailable(s))
        {
            ZeroMemory(messageBuffer, bufferSize);
            int bytesIn = recvfrom(s, messageBuffer, bufferSize, 0, (sockaddr*)&client, &clientLength);
            char clientIP[bufferSize];
            ZeroMemory(clientIP, bufferSize);
            inet_ntop(AF_INET, &client.sin_addr, clientIP, 256);
            return true;
        }
        return false;
    }

    std::string GetNetworkMessage()
    {
        std::string message = messageBuffer;
        return message;
    }

private:
    bool dataAvailable(int sock, int interval = 6000)
    {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(sock, &fds);
        timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = interval;

        return (select(sock + 1, &fds, 0, 0, &tv) == 1);
    }
private:
    SOCKET s;
    sockaddr_in client;
    int clientLength = sizeof(client);
    static constexpr int bufferSize = 512;
    unsigned short port;
    char messageBuffer[bufferSize] = {};

};

int main()
{
    //Create server object on the heap.
    std::unique_ptr<UDPServer> udp = std::make_unique<UDPServer>(6000);

    //Get some new threads mate.
    std::thread theThread;
    std::string oldString = "";
    while (true)
    {
        //Problems...
        theThread = std::thread{udp->Recieve()};
        if (udp->GetNetworkMessage() != oldString)
        {
            //print out any changed data we find.
            oldString = udp->GetNetworkMessage();
            std::cout << oldString << std::endl;
        }
    }   
}

【问题讨论】:

  • 多线程的关键是确保您拥有的数据保持在有效状态。例如不在一个线程中编辑变量,而另一个线程读取它。如果你想加快你的程序,具体的分析会有所帮助,例如如果大部分处理发生在ZeroMemory,您可以尝试避免不必要的分配和取消分配。如果您只想异步运行一个函数而没有太多开销,那么std::async 是一个不错的选择。
  • 不确定您的UDPServer 能否处理对Recieve 的线程访问。
  • theThread = std::thread{udp-&gt;Recieve()}; 每次循环时都会创建一个新的线程对象。线程的制作成本相对较高,我会创建一个线程池,以便只创建一次线程。然后主线程(执行选择和读取)将从套接字读取数据并移交要由池中的线程处理的数据包,使其立即返回侦听套接字上的更多数据包。
  • @MartinYork,关于我的 UDPServer 能够处理对 Receive 的线程访问,您几乎可以肯定是正确的哈哈。你能给我指出任何好的资源来了解线程池吗?快速的谷歌有很多东西,但它似乎是一个巨大的蠕虫罐头,我希望它是一个可靠的来源。

标签: c++ windows multithreading winsock


【解决方案1】:

您不清楚的项目之一是线程中的内存可访问性。在 Windows 以及可能的大多数其他操作系统中,主线程中可访问的任何内存也可由同一进程中的所有其他线程访问。

关于线程和内存有两个问题。第一个是多个线程如何知道给定变量或类在内存中的位置。这通常通过在创建新线程时传递一个指向新线程的指针来解决。大多数线程创建机制为此提供了一个参数。所以这是比较容易解决的问题。

更难解决的问题是确保一个线程在另一个线程使用它时不会更改变量或类。通常这是通过使用互斥同步对象来解决的,通常称为互斥锁或锁。我建议学习互斥锁的概念。但归根结底,它一次只允许一个线程访问由该互斥锁锁定的任何内容。因此,如果一个线程忙于更改或使用该对象,则另一个线程将等到另一个线程解锁该对象后再继续。

但是当您进入多个锁时,就会出现一种称为死锁的情况。一个简单的例子是:线程 A 持有锁 1 并等待访问锁 2。同时线程 B 持有锁 2 并等待访问锁 1。所以两个线程都在等待另一个线程。解决方案是,无论何时您必须持有两把锁,始终以相同的顺序使用它们。所以在这种情况下,如果两个线程总是先锁定 1 然后锁定 2,它们就不会死锁。

你想了解的主题是线程和线程同步。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-24
    • 2011-02-11
    • 2013-04-02
    • 2015-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多