delphi VCL显示问题 分线程与主线程的同步
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了delphi VCL显示问题 分线程与主线程的同步相关的知识,希望对你有一定的参考价值。
我知道Delphi中的VCL组件大多都是线程不安全的.现在遇到问题了,我在写一个程序的时候,创建线程是用creattheard,而不是TTheard类,其分网络线程需要读取网络数据,然后更新Treeview,前面运行无错,每次运行更新VC时,DELPHI就报错误,类似什么内存不能为read的错误.我怀疑是VCL的安全问题,原则上分线程是不能访问VCL的.可是我的分线程必须更新TREEVIW,而且在creatheard建的线程里面,我一使用Synchronize就会报错,请问该如何实现如主线程的同步?
帖出部分源码:
procedure SocketWorkThread(ns :TSocket);stdcall;
const
buflen=1024*8;
var
recvbuf :array[0..buflen -1] of Char;
rtn :Integer;
rs :string;
error :string;
ld:integer;
S1,dirn,filn,dd:string ;
sendbuf :array[0..buflen +90000] of Char;
sendlen:integer;
strr:string;
strs:string;
ds:integer;
i,j,k,m,n,l,o:integer; //循环变量
ListItem,listitems:TListItem;
send_str:string;
contral:string;
is_server:boolean;
contral_array,recv_array,bufs_array: array of string;
recv_str,contral_str,temp_str:string;
t,z,test:integer;
filemanage_hostnode, filemanage_disknode,filemanage_dicnode,regedit_keynode: TTreeNode;
send_socket:integer;
begin_down:boolean;
label
line3;
begin
try
while true do
(中间recv读取网络封包的源码省略,所有读取的内容全部存到了bufs_array数组中)
//----------根据控制指令执行相应操作
case strtoint(contral) of
0001:
0002:
... ...
0004: //添加目录列表
begin
filemanage_nodeclick.DeleteChildren;
for k:=0 to high(bufs_array) do
begin
filemanage_dicnode:=form1.treeview1.Items.AddChild(filemanage_nodeclick,bufs_array[k]);
New(pp);
pp^.treeview1_socket:= ns;
pp^.ii := 2;
pp^.tag :=PMyData(filemanage_nodeclick.Data)^.tag+bufs_array[k]+'\';
filemanage_dicnode.Data := pp;
filemanage_dicnode.ImageIndex := 5;
filemanage_dicnode.SelectedIndex :=6;
end;
filemanage_nodeclick.Expand(true);
node_lock:=false;
form1.timeout.Enabled:=false;
//form1.Memo1.Lines.Add('目录数量是'+inttostr(high(bufs_array)+1));
end;
0005://添加文件列表
begin
form1.listview2.Items.Clear;
for k:=0 to high(bufs_array) do
begin
form1.listview2.Items.Add.caption:=bufs_array[k] ;
form1.listview2.Items[k].imageindex:=image_index(bufs_array[k]);
form1.listview2.items[k].SubItems.Add(PMyData(form1.TreeView1.Selected.Data)^.tag+bufs_array[k]);
end;
//form1.Memo1.Lines.Add('文件数量是:'+inttostr(high(bufs_array)+1));
end;
... ....
其间添加文件列表,和添加目录列表会出错
请问该如何使用Synchronize???
有劳大侠指点一二!
哦,那也许问题不在VCL上,问题是内存地址出错?
是不是指针的问题?
至少不会出现“内存不能为read的错误”的错误,可能代码哪里写错了。
我想说的只是Synchronize和VCL同步没关系。
如果需要同步那就得同步。Synchronize只是同步的一种方法(更准确的说是提供一种在主线程中队列执行的方法),你可以使用任何其他的方法。
关键是FOnProgress做的是什么,如果仅仅是操作VCLSynchronize反而多此一举。操作VCL并不会冲突,不相信的话可以试一下。 参考技术A 不用没影响 是因为你线程少了.多线程.
Synchronize(这里是一个过程,没有参数的过程);
你要对vcl有什么操作,就写在这个过程里面.
因为delphi里面线程的Synchronize这个方法就是把线程中定义的这个方法的指针传给主线程,然后主线程调用这个方法来执行vcl操作,所以这样才保证了安全.
我举个例子吧.贴点代码,我用的TThread继承的类.
TDataStread = class(TThread)
private
FSock:TClientSocket;
FFile:TFileStream;
FTotalSize,FStartPos:Integer;
FHandle:THandle;
FProgress:TOnDownProgress;
pStr:TWinSocketStream;
procedure setDone;//这个就是我上面说的那个没有参数的过程.
protected
procedure execute;override;
public
constructor Create(bSuspend:Boolean;aSock:TClientSocket;aFile:TFileStream;var aStart,aTotal:Integer;ahandle:THandle;aProgress:TOnDownProgress); overload;
destructor Destroy; override;
end;
创建线程
aThread:=TDataStread.Create(True,FDataSock,FFileStream,FStartPos,FTotalSize,Self.handle,FOnProgress);
aThread.Resume;
aThread.OnTerminate:=freeThread;
FCtrlSock.Active:=False;
然后就是同步的地方
procedure TDataStread.execute;
var
p:array[0..24*1024 -1] of Char;
nOne:Integer;
begin
pStr:=TWinSocketStream.Create(FSock.Socket,6000);
while (not terminated)and(FSock.Active) do
begin
if pStr.WaitForData(6000) then
begin
try
if Boolean(pStr) then
nOne:=pStr.Read(p,24*1024);
//
if Boolean(FFile) then
begin
FFile.Seek(FStartPos,soFromBeginning);
Inc(FStartPos,FFile.Write(p,nOne));
end;
//
Synchronize(setDone);
if FStartPos=FTotalSize then
begin
SendMessage(FHandle,wm_fileDone,FStartPos,0);
Terminate;
end;
except
FreeAndNil(pStr);
end;
end
else
begin
SendMessage(FHandle,wm_fileDone,FStartPos,0);
Terminate;
end;
end;
end;
procedure TDataStread.setDone;
begin
if Assigned(FProgress) then
FProgress(Self,FStartPos,FTotalSize);
end;
将工作线程与主线程同步
【中文标题】将工作线程与主线程同步【英文标题】:Synchronize worker threads with a main thread 【发布时间】:2021-01-31 18:53:23 【问题描述】:如果工作线程可以生成另一个任务,如何正确地?我使用 std::queue 来维护由互斥锁和原子变量保护的任务来跟踪繁忙的线程。不幸的是,我在执行结束时遇到了死锁。
我已经从我的项目中提取代码并创建了以下示例(您可以使用 g++ 或 MSVC 轻松编译它):
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <stdexcept>
#include <functional>
#include <stack>
#include <atomic>
#include <queue>
template <class T, class Compare>
class USort
using Task = std::pair<T*, T*>;
private:
size_t m_ThreadsNum;
std::atomic<bool> m_Finished;
std::atomic<size_t> m_Busy;
std::thread* m_Threads;
std::queue<Task> m_Tasks;
size_t m_Size;
T* m_Data;
Compare m_Comparator;
std::condition_variable m_WaitFinished;
std::condition_variable m_WaitSorter;
std::mutex m_TaskQueueMutex;
private:
const size_t THREAD_THRESHOLD = 1024;
const size_t THREAD_POOL_THRESHOLD = 8192;
bool HasTask()
std::unique_lock<std::mutex> lock(m_TaskQueueMutex);
return m_Tasks.size() > 0;
bool PopTask(T** L, T** R)
std::unique_lock<std::mutex> lock(m_TaskQueueMutex);
if (m_Tasks.size() == 0)
*L = *R = nullptr;
return false;
*L = m_Tasks.front().first;
*R = m_Tasks.front().second;
m_Tasks.pop();
return true;
void PushTask(T* L, T* R)
std::unique_lock<std::mutex> lock(m_TaskQueueMutex);
m_Tasks.emplace(std::pair<T*, T*>(L, R));
m_WaitSorter.notify_one();
void SortThread(size_t Id)
std::mutex sorter_mutex;
for (;;)
std::unique_lock<std::mutex> lock(sorter_mutex);
///
/// ----------------------------------> some threads wait here
///
m_WaitSorter.wait(lock, [this]() return m_Finished || HasTask(); );
if (m_Finished) break;
m_Busy++;
T *left, *right;
while (PopTask(&left, &right))
Sort(left, right);
if (--m_Busy == 0)
m_WaitFinished.notify_one();
// just simulate work
void Sort(T* Left, T* Right)
if (Right - Left > 10)
PushTask(Left, Right-10);
void WaitForSortingIsFinished()
std::mutex finished;
std::unique_lock<std::mutex> lock(finished);
m_WaitFinished.wait(lock, [this]() return m_Busy == 0 && !HasTask(); );
void FinishThreads()
m_Finished = true;
m_WaitSorter.notify_all();
void ReleaseThreads()
if (m_Threads)
for (size_t i = 0; i < m_ThreadsNum; i++)
///
/// ----------------------------------> main thread stuck here
///
m_Threads[i].join();
delete[] m_Threads;
m_Threads = nullptr;
public:
USort(size_t NumberOfThreads = 0) : m_Comparator(Compare())
if (NumberOfThreads == 0)
static const unsigned int max_concurrency = std::thread::hardware_concurrency();
NumberOfThreads = max_concurrency;
if (NumberOfThreads == 0) NumberOfThreads = 4;
m_Finished = false;
m_ThreadsNum = NumberOfThreads;
m_Threads = nullptr;
~USort()
ReleaseThreads();
void Sort(T* Data, size_t Size)
// build thread pool
m_Threads = new std::thread[m_ThreadsNum];
for (size_t i = 0; i < m_ThreadsNum; i++)
m_Threads[i] = std::thread(&USort::SortThread, this, i);
// process data
PushTask(Data, Data + Size - 1);
WaitForSortingIsFinished();
FinishThreads();
;
template <class T, class Compare>
void usort(T* Data, size_t Size, size_t NumberOfThreads = 0)
USort<T, Compare> mt_sorter(NumberOfThreads);
mt_sorter.Sort(Data, Size);
const size_t ARR_SIZE = 0x00010000;
struct comp
bool operator()(const int& L, const int& R) const
return L < R;
;
int main()
int* arr = new int[ARR_SIZE];
for (int i = 0; i < ARR_SIZE; i++)
arr[i] = rand() % 3200000;
usort<int, comp>(arr, ARR_SIZE, 16);
delete[] arr;
return 0;
问题是,在我的示例中,线程并不总是完成。 m_WaitSorter.wait()
中不时有一些线程挂起,因此m_Threads[i].join();
中的主线程挂起。逻辑缺陷在哪里。为什么调用FinishThreads()
没有完成所有线程?
编辑: 基本上我想实现多线程排序算法。
-
主线程创建线程池,将第一个任务(排序整个数组)推送到任务队列,等待排序完成
池线程接受任务,将其划分为更小的任务(1-3)。其中一项任务立即由当前池线程处理,其他任务被推送到队列中
池线程必须在整个数据集排序后才能完成(队列中没有任务并且所有池线程都处于挂起状态)
当排序完成后,主线程应该被唤醒
主线程应该完成挂起的线程
因此,从我的角度来看,我需要两个 conditional_variabes,在主线程中带有谓词“所有线程都挂起 && 在队列中没有任务”,在池线程中需要“在队列中有任务 || 完成线程”。
【问题讨论】:
你实际上误用了同步对象,思想一定不能满足同步对象的接口。条件变量必须以某种方式与它所使用的对象和互斥体连接。请更准确地解释您愿意实现的目标,您要解决的任务是什么? 好的,我已经详细阐述了我的问题 【参考方案1】:好的,我已经仔细阅读了文档,并在我的代码中发现了一个错误。对 notify_one()
、notify_all()
和 wait()
的调用必须通过相同的 mutext 进行控制。考虑到这一点,我更新并简化了我的代码:
bool WaitAndPopTask(T** L, T** R)
std::unique_lock<std::mutex> lock(m_TaskQueueMutex);
m_WaitSorter.wait(lock, [this]() return m_Finished || !m_Tasks.empty(); );
if (m_Finished) return false;
m_Busy++;
*L = m_Tasks.front().first;
*R = m_Tasks.front().second;
m_Tasks.pop();
return true;
void SortThread(size_t Id)
for (;;)
T *left, *right;
if (!WaitAndPopTask(&left, &right)) break;
Sort(left, right);
std::lock_guard<std::mutex> lk(m_TaskQueueMutex);
if (--m_Busy == 0 && m_Tasks.empty())
FinishThreads();
void Sort(T* Data, size_t Size)
// build thread pool
m_Threads = new std::thread[m_ThreadsNum];
for (size_t i = 0; i < m_ThreadsNum; i++)
m_Threads[i] = std::thread(&USort::SortThread, this, i);
// process data
PushTask(Data, Data + Size - 1);
ReleaseThreads();
【讨论】:
以上是关于delphi VCL显示问题 分线程与主线程的同步的主要内容,如果未能解决你的问题,请参考以下文章
delphi 多线程之 TEvent 和 TLightweightEvent
delphi 多线程之System.TMonitor (续一)