這篇文章主要講解了“怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步”吧!
創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括大峪網(wǎng)站建設(shè)、大峪網(wǎng)站制作、大峪網(wǎng)頁(yè)制作以及大峪網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,大峪網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到大峪省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
20210127:在生產(chǎn)者、消費(fèi)者的方法中添加線程掛起方法QThread::usleep(10),使ui不卡。
20210128:在添加Track類(保存生產(chǎn)者Producer生成的每組數(shù)據(jù)),在ui界面中使用model-view同步顯示生產(chǎn)者生成的數(shù)據(jù),model-view不會(huì)對(duì)主線程造成卡頓。對(duì)消費(fèi)者同樣創(chuàng)建view,還沒(méi)有進(jìn)行model綁定。
避免引起主線程的阻塞,Qt在子線程中處理大數(shù)據(jù),當(dāng)多個(gè)子線程需要處理同一塊數(shù)據(jù)時(shí),需要使用數(shù)據(jù)同步,避免出現(xiàn)調(diào)用錯(cuò)亂情況,在這里我們?cè)趦蓚€(gè)子線程使用QSemaphore作為標(biāo)志位,對(duì)數(shù)組進(jìn)行標(biāo)識(shí),生產(chǎn)者線程將生成的資源存入數(shù)組,消費(fèi)者數(shù)組消耗數(shù)組內(nèi)的資源,當(dāng)有一方的速度過(guò)快導(dǎo)致數(shù)組資源耗盡時(shí),該子線程被阻塞,直到有資源時(shí)子線程繼續(xù)。代碼如下:
在全局變量中聲明數(shù)組、數(shù)組大小、資源總量:
constant_variable.h
#ifndef CONSTANT_VARIABLE_H #define CONSTANT_VARIABLE_H // 所有變量在h文件中均是聲明,定義在cpp文件中 /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-24 @brief h文件聲明extern變量,cpp文件定義變量 ******************************************************************************************/ #include <QSemaphore> /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-24 @brief 設(shè)置循環(huán)保存數(shù)據(jù)的數(shù)組大小,相當(dāng)于設(shè)置“緩存”大小 ***************************************************************************/ extern const int BUFFER_SIZE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 要讀取的總資源數(shù)量,數(shù)據(jù)量大,無(wú)法一次讀取完,需要長(zhǎng)時(shí)間,分批讀取 ***************************************************************************/ extern const int DATA_SIZE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 循環(huán)使用,保存數(shù)據(jù)資源的數(shù)組 ***************************************************************************/ extern char BUFFER[]; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 標(biāo)識(shí)Producer可以保存資源的空余位置的數(shù)量,保存在數(shù)組中的數(shù)據(jù)量,因?yàn)槌? 始狀態(tài)數(shù)組中沒(méi)有任何數(shù)據(jù),數(shù)組有DATA_SIZE個(gè)資源可用 ***************************************************************************/ extern QSemaphore PRODUCER_SPACE; /*************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @date 2021-01-24 @brief 標(biāo)識(shí)Consumer可以使用的已保存資源的數(shù)量,因?yàn)槌跏紩r(shí)沒(méi)有數(shù)據(jù)可供Consumer 使用,初始資源有0個(gè) ***************************************************************************/ extern QSemaphore CONSUMER_SPACE; #endif // CONSTANT_VARIABLE_H
constant_variable.cpp
#include "constant_variable.h" const int BUFFER_SIZE(4096); const int DATA_SIZE(100000); // 定義數(shù)組大小為BUFFER_SIZE char BUFFER[BUFFER_SIZE]; // 初始時(shí)可用資源位BUFFER_SIZE QSemaphore PRODUCER_SPACE(BUFFER_SIZE); QSemaphore CONSUMER_SPACE(0);
創(chuàng)建窗體:
Producer按鈕生成資源,保存到數(shù)組中,在左側(cè)文本框中顯示Producer所在的線程及生成的資源。Consumer按鈕消耗數(shù)組中的資源,在左側(cè)文本框中顯示Consumer所在的線程及消耗的資源。這兩個(gè)按鈕是單獨(dú)運(yùn)行。Both_start_PushButton按鈕同時(shí)啟動(dòng)兩個(gè)子線程,一個(gè)生產(chǎn)資源,一個(gè)消耗資源,生產(chǎn)和消耗的都在左邊的文本框中顯示。
窗口代碼如下:
producer_consumer_dialog.ui 代碼:
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>Producer_consumer_dialog</class> <widget class="QDialog" name="Producer_consumer_dialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>722</width> <height>451</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPlainTextEdit" name="producer_plainTextEdit"/> </item> <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QPushButton" name="producer_pushButton"> <property name="text"> <string>Producer</string> </property> </widget> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Expanding</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </item> <item> <widget class="QTableView" name="producer_tableView"/> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPlainTextEdit" name="consumer_plainTextEdit"/> </item> <item> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QPushButton" name="consumer_pushButton"> <property name="text"> <string>Consumer</string> </property> </widget> </item> <item> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </item> <item> <widget class="QTableView" name="consumer_tableView"/> </item> </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>348</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="both_start_pushButton"> <property name="text"> <string>Both_start_PushButton</string> </property> </widget> </item> </layout> </item> </layout> </widget> <resources/> <connections/> </ui>
producer_consumer_dialog.h 文件:
#ifndef PRODUCER_CONSUMER_DIALOG_H #define PRODUCER_CONSUMER_DIALOG_H #include <QDialog> #include <QThread> QT_BEGIN_NAMESPACE class Producer_move_to_thread; class Consumer_move_to_thread; class Producer_table_model; //class QCloseEvent; QT_END_NAMESPACE namespace Ui { class Producer_consumer_dialog; } /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 創(chuàng)建主窗口,生成創(chuàng)造者producer,并在子線程-1生產(chǎn)資源。生成消費(fèi)者consumer,并在子線程-2消耗生 產(chǎn)資源。資源均存儲(chǔ)在BUFFER[]中 ******************************************************************************************/ class Producer_consumer_dialog : public QDialog { Q_OBJECT public: explicit Producer_consumer_dialog(QWidget *parent = nullptr); ~Producer_consumer_dialog(); protected: virtual void closeEvent(QCloseEvent* ev); private: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 初始化窗體控件 ***************************************************************************/ void init_widgets(); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 初始化私有變量,將producer_,consumer_放入各自的子線程 ***************************************************************************/ void init_instances(); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 實(shí)例化信號(hào)槽 ***************************************************************************/ void init_connections(); private slots: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 將字符串content設(shè)置到文本框producer_plainTextEdit @param content 字符串 ***************************************************************************/ void set_producer_plain_text(quint32 serial_number, quint64 thread_id, char ch); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-27 @brief 將字符串content設(shè)置到文本框consumer_plainTextEdit @param content 字符串 ***************************************************************************/ void set_consumer_plain_text(QString content); private: Ui::Producer_consumer_dialog *ui; Producer_move_to_thread* producer_; Consumer_move_to_thread* consumer_; QThread producer_thread_; QThread consumer_thread_; /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 消費(fèi)者產(chǎn)生數(shù)據(jù)對(duì)應(yīng)的模型 ***************************************************************************/ Producer_table_model* producer_table_model_; }; #endif // PRODUCER_CONSUMER_DIALOG_H
producer_consumer_dialog.cpp文件:
#include "producer_consumer_dialog.h" #include "ui_producer_consumer_dialog.h" #include "producer_move_to_thread.h" #include "consumer_move_to_thread.h" #include "producer_table_model.h" Producer_consumer_dialog::Producer_consumer_dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Producer_consumer_dialog) { ui->setupUi(this); init_widgets(); init_instances(); init_connections(); } Producer_consumer_dialog::~Producer_consumer_dialog() { delete producer_; delete consumer_; delete producer_table_model_; delete ui; } void Producer_consumer_dialog::closeEvent(QCloseEvent *ev) { // QThread::sleep(3); if ( producer_thread_.isRunning()) { producer_thread_.quit(); producer_thread_.wait(); } if ( consumer_thread_.isRunning()) { consumer_thread_.quit(); consumer_thread_.wait(); } QDialog::closeEvent(ev); } void Producer_consumer_dialog::init_widgets() { // setFixedSize(sizeHint()); resize(sizeHint()); } void Producer_consumer_dialog::init_instances() { producer_ = new Producer_move_to_thread(); producer_->moveToThread(&producer_thread_); producer_thread_.start(); consumer_ = new Consumer_move_to_thread(); consumer_->moveToThread(&consumer_thread_); consumer_thread_.start(); producer_table_model_ = new Producer_table_model(); ui->producer_tableView->setModel(producer_table_model_); } void Producer_consumer_dialog::init_connections() { connect(ui->producer_pushButton, &QPushButton::clicked, producer_, &Producer_move_to_thread::create_char_in_thread); connect(ui->consumer_pushButton, &QPushButton::clicked, consumer_, &Consumer_move_to_thread::create_char_in_thread); connect(ui->both_start_pushButton, &QPushButton::clicked, ui->producer_pushButton, &QPushButton::clicked); connect(ui->both_start_pushButton, &QPushButton::clicked, ui->consumer_pushButton, &QPushButton::clicked); // 獲取producer_生成的字符串 connect(producer_, &Producer_move_to_thread::signal_index_id_content, this, &Producer_consumer_dialog::set_producer_plain_text); connect(producer_, &Producer_move_to_thread::signal_index_id_content, producer_table_model_, &Producer_table_model::add_data_to_track_list); // 獲取consumer_取得的字符串 connect(consumer_, &Consumer_move_to_thread::signal_string, this, &Producer_consumer_dialog::set_consumer_plain_text); } void Producer_consumer_dialog::set_producer_plain_text(quint32 serial_number, quint64 thread_id, char ch) { QString content = QString("%1: Producer thread id: %2, char: %3"). arg(serial_number). arg(thread_id). arg(ch); ui->producer_plainTextEdit->appendPlainText(content); // ui->producer_plainTextEdit->setPlainText(content); } void Producer_consumer_dialog::set_consumer_plain_text(QString content) { ui->consumer_plainTextEdit->appendPlainText(content); // ui->consumer_plainTextEdit->setPlaceholderText(content); }
生產(chǎn)類
producer_move_to_thread.h 文件:
#ifndef PRODUCER_MOVE_TO_THREAD_H #define PRODUCER_MOVE_TO_THREAD_H #include <QObject> /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 生產(chǎn)者,會(huì)被移動(dòng)到子線程中產(chǎn)生資源 ******************************************************************************************/ class Producer_move_to_thread : public QObject { Q_OBJECT public: explicit Producer_move_to_thread(QObject *parent = nullptr); ~Producer_move_to_thread(); signals: // void signal_string(quint32 serial_number, quint64 thread_id, char ch); void signal_index_id_content(quint32 index, int id, char ch); public slots: void create_char_in_thread(); }; #endif // PRODUCER_MOVE_TO_THREAD_H
producer_move_to_thread.cpp 文件:
#include <QtDebug> #include <QThread> #include <QtGlobal> #include "producer_move_to_thread.h" #include "constant_variable.h" Producer_move_to_thread::Producer_move_to_thread(QObject* parent): QObject(parent) { } Producer_move_to_thread::~Producer_move_to_thread() { } void Producer_move_to_thread::create_char_in_thread() { for (int index(0); DATA_SIZE != index; ++index) { // 生產(chǎn)1個(gè)資源(總共要產(chǎn)生的DATA_SZIE個(gè)資源),然后保存到BUFFER數(shù)組,BUFFER數(shù)組空余位置少1個(gè) PRODUCER_SPACE.acquire(1); // 計(jì)算獲取資源序號(hào)、線程號(hào)、數(shù)據(jù)資源 quint32 serial_number = (quint32) (index + 1); quint64 thread_id = (quint64) QThread::currentThreadId(); char ch = "ABCDXYZ"[uint(std::rand()) % 7]; BUFFER[index % BUFFER_SIZE] = ch; qDebug()<< "Produce thread id: " << thread_id << ", char: " << ch; // 將數(shù)據(jù)傳給窗體Producer_consumer_dialog // emit signal_string(serial_number, thread_id, ch); emit signal_index_id_content(serial_number, thread_id, ch); // 線程掛起,不對(duì)主線程造成卡頓 QThread::usleep(10); // 數(shù)組中已保存1個(gè)字符,Consumer獲取1個(gè)可用資源 CONSUMER_SPACE.release(1); } }
資源消耗類
consumer_move_to_thread.h 文件:
#ifndef CONSUMER_MOVE_TO_THREAD_H #define CONSUMER_MOVE_TO_THREAD_H #include <QObject> class Consumer_move_to_thread : public QObject { Q_OBJECT public: explicit Consumer_move_to_thread(QObject *parent = nullptr); signals: void signal_string(QString content); public slots: void create_char_in_thread(); }; #endif // CONSUMER_MOVE_TO_THREAD_H
consumer_move_to_thread.cpp文件:
#include <QtDebug> #include <QThread> #include "consumer_move_to_thread.h" #include "constant_variable.h" Consumer_move_to_thread::Consumer_move_to_thread(QObject *parent) : QObject(parent) { } void Consumer_move_to_thread::create_char_in_thread() { for (int index(0); DATA_SIZE != index; ++index) { // 消耗1個(gè)資源(總共要消耗DATA_SZIE個(gè)資源),從BUFFER數(shù)組中取出,BUFFER數(shù)組空余位置多1個(gè)。當(dāng) // 沒(méi)有資源提供時(shí),進(jìn)程被掛起,待有足夠資源后再執(zhí)行后續(xù)程序 CONSUMER_SPACE.acquire(1); qDebug()<< "Consumer thread id: " << (quint64) QThread::currentThreadId() << ", char: " << (char) BUFFER[index % BUFFER_SIZE]; QString content = QString("%1: Consumer thread id: %2, char: %3"). arg(index + 1). arg((quint64) QThread::currentThreadId()). arg(BUFFER[index % BUFFER_SIZE]); emit signal_string(content); //線程掛起,不對(duì)主線程造成卡頓 QThread::usleep(10); // 使用數(shù)組中1個(gè)資源,數(shù)組釋放1個(gè)空間,給生產(chǎn)者提供1個(gè)空余位置 PRODUCER_SPACE.release(1); } }
保存生產(chǎn)者生成的數(shù)據(jù)Track類:
Track.h
#ifndef TRACK_H #define TRACK_H #include <QtGlobal> /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 數(shù)據(jù)類,保存隨機(jī)生成的資源 ******************************************************************************************/ class Track { public: /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 構(gòu)造函數(shù) @param index 數(shù)據(jù)的生成序列號(hào) @param thread_id 生成數(shù)據(jù)的線程號(hào) @param ch 生成的資源數(shù)據(jù) ***************************************************************************/ explicit Track(quint32 index = 0, quint64 thread_id = 0, char ch = 'A'); ~Track(); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 拷貝構(gòu)造函數(shù) ***************************************************************************/ Track(const Track& r_value); /*************************************************************************** @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief 賦值操作符 ***************************************************************************/ Track& operator = (const Track& r_value); quint32 get_index() const; quint64 get_thread_id() const; char get_char() const; private: quint32 index_; quint64 thread_id_; char char_; }; #endif // TRACK_H
Track.cpp
#include "track.h" Track::Track(quint32 index, quint64 thread_id, char ch): index_(index), thread_id_(thread_id), char_(ch) { } Track::~Track() { } Track::Track(const Track &r_value) { index_ = r_value.index_; thread_id_ = r_value.thread_id_; char_ = r_value.char_; } Track &Track::operator =(const Track &r_value) { if (this != &r_value) { index_ = r_value.index_; thread_id_ = r_value.thread_id_; char_ = r_value.char_; } return *this; } quint32 Track::get_index() const { return index_; } quint64 Track::get_thread_id() const { return thread_id_; } char Track::get_char() const { return char_; }
生產(chǎn)者模型Produer_table_model類:
Produer_table_model.h
#ifndef PRODUCER_TABLE_MODEL_H #define PRODUCER_TABLE_MODEL_H #include <QAbstractTableModel> #include <QObject> #include "track.h" /***************************************************************************************** @copyright 2013-2020 @author qiaowei @contact weiweiqiao@126.com @version 1.0 @date 2021-01-28 @brief ******************************************************************************************/ class Producer_table_model : public QAbstractTableModel { Q_OBJECT public: explicit Producer_table_model(QObject* parent = nullptr); ~Producer_table_model(); void add_data_to_track_list(quint32 serial_number, quint64 thread_id, char ch); QList<Track>* get_track_list(); protected: virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; private: QList<Track>* track_list_; }; #endif // PRODUCER_TABLE_MODEL_H
Produer_table_model.cpp
#include "producer_table_model.h" Producer_table_model::Producer_table_model(QObject* parent): QAbstractTableModel(parent), track_list_(new QList<Track>()) { } Producer_table_model::~Producer_table_model() { delete track_list_; } void Producer_table_model::add_data_to_track_list(quint32 serial_number, quint64 thread_id, char ch) { track_list_->append(Track(serial_number, thread_id, ch)); QModelIndex header_model_index = createIndex(serial_number - 1, 0); QModelIndex tail_model_index = createIndex(serial_number - 1, 2); layoutChanged(); } QList<Track> *Producer_table_model::get_track_list() { return track_list_; } int Producer_table_model::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) if ( track_list_) { return track_list_->count(); } else { return 0; } } int Producer_table_model::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) // // 序號(hào)、線程號(hào)、數(shù)據(jù)資源 // return 3; // 線程號(hào)、數(shù)據(jù)資源 return 2; } QVariant Producer_table_model::data(const QModelIndex &index, int role) const { if ( !index.isValid()) { return QVariant(); } QVariant v; if (Qt::DisplayRole == role) { switch (index.column()) { case 0: v = track_list_->at(index.row()).get_thread_id(); break; case 1: v = (QChar) track_list_->at(index.row()).get_char(); break; } return v; } return QVariant(); } QVariant Producer_table_model::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation)) { QString header_content; switch (section) { case 0: header_content = tr("線程號(hào)"); break; case 1: header_content = tr("數(shù)據(jù)"); break; } return header_content; } return QAbstractTableModel::headerData(section, orientation, role); }
在這里特別注意PRODUCER_SPACE和CONSUMER_SPACE的初始值,因?yàn)橘Y源BUFFER數(shù)組初始資源為0,所以生產(chǎn)者Producer可用的數(shù)組空間為所有,消耗著Consumer可用的數(shù)組空間為0,因?yàn)闆](méi)有資源可用。
運(yùn)行結(jié)果如下圖:
感謝各位的閱讀,以上就是“怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
網(wǎng)頁(yè)標(biāo)題:怎么使用QSemaphore進(jìn)行多線程數(shù)據(jù)同步
文章網(wǎng)址:http://www.rwnh.cn/article42/jeesec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)、動(dòng)態(tài)網(wǎng)站、建站公司、云服務(wù)器、虛擬主機(jī)、網(wǎng)站營(yíng)銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)