C++ 使用线程时出现意外输出
我正在为一个游戏开发一个概念验证测试程序,其中某些动作是线程化的,并且每个线程的信息都输出到命令窗口。到目前为止,我已经完成了基本的线程处理,但似乎调用函数中的couting并不是为每个线程编写的,而是每个线程都覆盖了其他线程的输出 期望的或预期的输出是,每个线程将输出mLaser的mCycle函数中提供的信息。本质上,这意味着为每个对象设置一个排序计时器,倒计时直到该对象完成其任务。每个线程都应该有一个输出,所以如果有五个线程在运行,那么应该有五个计数器独立地倒计时 当前输出是这样的:每个线程在同一空间中输出自己的信息,然后覆盖另一个线程试图输出的内容 以下是程序当前输出的示例: 周期结束前的时间周期74结束前的时间:36结束: 92秒2秒按任意键继续 如果您检查信息是如何从McCycle导出的,您可以看到数字和其他文本位于不应该出现的位置的畸变 应显示的内容比这些行更长: 循环1完成前的时间: 92秒 循环2完成前的时间: 112秒 循环3完成前的时间: 34秒 循环4已经完成 我不确定这是否是由于某种线程锁定导致的,这种锁定是由于我的代码是如何构造的,还是因为我对输出编码的疏忽。如果我能得到一双新的眼睛来检查代码并指出任何可能是错误的地方,我将不胜感激 这是我的代码,它应该可以在任何MSVS2013安装中编译(没有使用自定义库)C++ 使用线程时出现意外输出,c++,multithreading,command-line-interface,C++,Multithreading,Command Line Interface,我正在为一个游戏开发一个概念验证测试程序,其中某些动作是线程化的,并且每个线程的信息都输出到命令窗口。到目前为止,我已经完成了基本的线程处理,但似乎调用函数中的couting并不是为每个线程编写的,而是每个线程都覆盖了其他线程的输出 期望的或预期的输出是,每个线程将输出mLaser的mCycle函数中提供的信息。本质上,这意味着为每个对象设置一个排序计时器,倒计时直到该对象完成其任务。每个线程都应该有一个输出,所以如果有五个线程在运行,那么应该有五个计数器独立地倒计时 当前输出是这样的:每个线程
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
类激光器
{
公众:
mLaser(内部控制、浮动控制)
{
mlCLen=clen;
mlMAmt=mamt;
}
int getCLen()
{
返回mlCLen;
}
浮点getMAmt()
{
返回mlMAmt;
}
void mCycle(int i1,int mCLength)
{
bool bMCycle=true;
int mCTime_left=mCLength*1000;
int mCTime_start=GetTickCount();//获取周期开始时间
int mCTime_old=((mCTime_start+500)/1000);
cout虽然从多个线程并发写入std::cout
必须是无数据竞争的,但不能保证并发写入不会交错。我不确定一个线程的一个写入操作是否可以与另一个线程的一个写入操作交错,但它们肯定可以在写入操作之间交错交叉(我认为来自不同线程的单个输出可以交叉)
本标准关于并发访问标准流对象(即std::cout
,std::cin
等)的说明见27.4.1[iostream.objects.overview]第4段:
多线程对同步(27.5.3.4)标准iostream对象的格式化和未格式化输入(27.7.2.1)和输出(27.7.3.1)函数或标准C流的并发访问不得导致数据竞争(1.10)。[注:如果用户希望避免交叉字符,则必须由多个线程同步这些对象和流的并发使用。-结束注]
如果你想让输出以某种形式出现,你需要同步对
std::cout
的访问,例如,使用互斥锁。虽然从多个线程同时写入std::cout
必须是无数据竞争的,但不能保证并发写入不会交错。我不确定是否有一次写入一个线程的操作可以与另一个线程的一个写操作交错,但它们肯定可以在写操作之间交错(我认为不同线程的单个输出可以交错)
本标准关于并发访问标准流对象(即std::cout
,std::cin
等)的说明见27.4.1[iostream.objects.overview]第4段:
多线程对同步(27.5.3.4)标准iostream对象的格式化和未格式化输入(27.7.2.1)和输出(27.7.3.1)函数或标准C流的并发访问不得导致数据竞争(1.10)。[注:如果用户希望避免交叉字符,则必须由多个线程同步这些对象和流的并发使用。-结束注]
如果你想让输出以某种形式出现,你需要同步对
std::cout
的访问,例如,使用互斥锁。虽然从多个线程同时写入std::cout
必须是无数据竞争的,但不能保证并发写入不会交错。我不确定是否有一次写入一个线程的操作可以与另一个线程的一个写操作交错,但它们肯定可以在写操作之间交错(我认为不同线程的单个输出可以交错)
本标准关于并发访问标准流对象(即std::cout
,std::cin
等)的说明见27.4.1[iostream.objects.overview]第4段:
多线程对同步(27.5.3.4)标准iostream对象的格式化和未格式化输入(27.7.2.1)和输出(27.7.3.1)函数或标准C流的并发访问不得导致数据竞争(1.10)。[注:如果用户希望避免交叉字符,则必须由多个线程同步这些对象和流的并发使用。-结束注]
如果您希望以某种形式显示输出
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <random>
#include <thread>
#include <future>
using namespace std;
class mLaser
{
public:
mLaser(int clen, float mamt)
{
mlCLen = clen;
mlMAmt = mamt;
}
int getCLen()
{
return mlCLen;
}
float getMAmt()
{
return mlMAmt;
}
void mCycle(int i1, int mCLength)
{
bool bMCycle = true;
int mCTime_left = mCLength * 1000;
int mCTime_start = GetTickCount(); //Get cycle start time
int mCTime_old = ((mCTime_start + 500) / 1000);
cout << "Time until cycle " << i1 << " is complete: " << endl;
while (bMCycle)
{
cout << ((mCTime_left + 500) / 1000) << " seconds";
bool bNChange = true;
while (bNChange)
{
//cout << ".";
int mCTime_new = GetTickCount();
if (mCTime_old != ((mCTime_new + 500) / 1000))
{
//cout << mCTime_old << " " << ((mCTime_new+500)/1000) << endl;
mCTime_old = ((mCTime_new + 500) / 1000);
mCTime_left -= 1000;
bNChange = false;
}
}
cout << " \r" << flush;
if (mCTime_left == 0)
{
bMCycle = false;
}
}
cout << "Mining Cycle " << i1 << " finished" << endl;
system("Pause");
return true;
}
private:
int mlCLen;
float mlMAmt;
};
string sMCycle(mLaser ml, int i1, thread& thread);
int main()
{
vector<mLaser> mlasers;
vector<thread> mthreads;
future<string> futr;
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> laser(1, 3);
uniform_int_distribution<> cLRand(30, 90);
uniform_real_distribution<float> mARand(34.0f, 154.3f);
int lasers;
int cycle_time;
float mining_amount;
lasers = laser(gen);
for (int i = 0; i < lasers-1; i++)
{
mlasers.push_back(mLaser(cLRand(gen), mARand(gen)));
mthreads.push_back(thread());
}
for (int i = 0; i < mlasers.size(); i++)
{
futr = async(launch::async, [mlasers, i, &mthreads]{return sMCycle(mlasers.at(i), i + 1, mthreads.at(i)); });
//mthreads.at(i) = thread(bind(&mLaser::mCycle, ref(mlasers.at(i)), mlasers.at(i).getCLen(), mlasers.at(i).getMAmt()));
}
for (int i = 0; i < mthreads.size(); i++)
{
//mthreads.at(i).join();
}
//string temp = futr.get();
//float out = strtof(temp.c_str(),NULL);
//cout << out << endl;
system("Pause");
return 0;
}
string sMCycle(mLaser ml, int i1, thread& t1)
{
t1 = thread(bind(&mLaser::mCycle, ref(ml), ml.getCLen(), ml.getMAmt()));
//t1.join();
return "122.0";
}
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <random>
#include <thread>
#include <future>
using namespace std; //Tacky, but good enough fo a poc D:
class mLaser
{
public:
mLaser(int clen, float mamt, int time_left)
{
mlCLen = clen;
mlMAmt = mamt;
mCTime_left = time_left;
bIsCompleted = false;
}
int getCLen()
{
return mlCLen;
}
float getMAmt()
{
return mlMAmt;
}
void setMCOld(int old)
{
mCTime_old = old;
}
void mCycle()
{
if (!bIsCompleted)
{
int mCTime_new = GetTickCount(); //Get current tick count for comparison to mCOld_time
if (mCTime_old != ((mCTime_new + 500) / 1000)) //Do calculations to see if time has passed since mCTime_old was set
{
//If it has then update mCTime_old and remove one second from mCTime_left.
mCTime_old = ((mCTime_new + 500) / 1000);
mCTime_left -= 1000;
}
cur_time = mCTime_left;
}
else
{
mCTime_left = 0;
}
}
int getCTime()
{
return cur_time;
}
int getCTLeft()
{
return mCTime_left;
}
void mCComp()
{
bIsCompleted = true;
}
bool getCompleted()
{
return bIsCompleted;
}
private:
int mlCLen; //Time of a complete mining cycle
float mlMAmt; //Amoung of ore produced by one mining cycle (not used yet)
int cur_time; //The current time remaining in the current mining cycle; will be removing this as it is just a copy of mCTime_left that I was going to use for another possiblity to make this code work
int mCTime_left; //The current time remaining in the current mining cycle
int mCTime_old; //The last time that mCycle was called
bool bIsCompleted; //Flag to check if a mining cycle has already been accounted for as completed
};
void sMCycle(mLaser& ml, int i1, thread& _thread); //Start a mining cycle thread
//Some global defines
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> laser(1, 10); //A random range for the number of mlaser entities to use
uniform_int_distribution<> cLRand(30, 90); //A random time range in seconds of mining cycle lengths
uniform_real_distribution<float> mARand(34.0f, 154.3f); //A random float range of the amount of ore produced by one mining cycle (not used yet)
int main()
{
//Init some variables for later use
vector<mLaser> mlasers; //Vector to hold mlaser objects
vector<thread> mthreads; //Vector to hold threads
vector<shared_future<int>> futr; //Vector to hold shared_futures (not used yet, might not be used if I can get the code working like this)
int lasers; //Number of lasers to create
int cycle_time; //Mining cycle time
int active_miners = 0; //Number of active mining cycle threads (one for each laser)
float mining_amount; //Amount of ore produced by one mining cycle (not used yet)
lasers = laser(gen); //Get a random number
active_miners = lasers; //Set this to that random number for the while loop later on
//Create the mlaser objects and push them into the mlasers vector
for (int i = 0; i < lasers; i++)
{
int clength = cLRand(gen);
mlasers.push_back(mLaser(clength, mARand(gen), (clength * 1000)));
//Also push thread obects into mthreads for each laser object
mthreads.push_back(thread());
}
//Setup data for mining cycles
for (int i = 0; i < mlasers.size(); i++)
{
int mCTime_start = GetTickCount(); //Get cycle start time
mlasers.at(i).setMCOld(((mCTime_start + 500) / 1000));
}
//Print initial display for mining cycles
for (int i = 0; i < mlasers.size(); i++)
{
cout << "Mining Laser " << i + 1 << " cycle will complete in " << (mlasers.at(i).getCTLeft() + 500) / 1000 << " seconds..." << endl;
}
while (active_miners > 0)
{
for (int i = 0; i < mlasers.size(); i++)
{
//futr.push_back(async(launch::async, [mlasers, i, &mthreads]{return sMCycle(mlasers.at(i), i + 1, mthreads.at(i)); }));
async(launch::async, [&mlasers, i, &mthreads]{return sMCycle(mlasers.at(i), i + 1, mthreads.at(i)); }); //Launch a thread for the current mlaser object
//mthreads.at(i) = thread(bind(&mLaser::mCycle, ref(mlasers.at(i)), mlasers.at(i).getCLen(), mlasers.at(i).getMAmt()));
}
//Output information from loops
//cout << " \r" << flush; //Return cursor to start of line and flush the buffer for the next info
system("CLS");
for (int i = 0; i < mlasers.size(); i++)
{
if (mlasers.at(i).getCTLeft() != 0) //If mining cycle is not completed
{
cout << "Mining Laser " << i + 1 << " cycle will complete in " << (mlasers.at(i).getCTLeft() + 500) / 1000 << " seconds..." << endl;
}
else if (mlasers.at(i).getCTLeft() == 0) //If it is completed
{
if (!mlasers.at(i).getCompleted())
{
mlasers.at(i).mCComp();
active_miners -= 1;
}
cout << "Mining Laser " << i + 1 << " has completed its mining cycle!" << endl;
}
}
}
/*for (int i = 0; i < mthreads.size(); i++)
{
mthreads.at(i).join();
}*/
//string temp = futr.get();
//float out = strtof(temp.c_str(),NULL);
//cout << out << endl;
system("Pause");
return 0;
}
void sMCycle(mLaser& ml, int i1,thread& _thread)
{
//Start thread
_thread = thread(bind(&mLaser::mCycle, ref(ml)));
//Join the thread
_thread.join();
}