PDA

View Full Version : سوال: Thread Safe کردن یک کلاس کامل



UfnCod3r
سه شنبه 30 مهر 1392, 09:25 صبح
سلام برفرض من یه کلاس لیست پیوندی دارم که چند تا تابع داره . addNode, getNodeCount, deleteNode
الان هر تابع رو Thred Safe کردم .( با CRITICAL_SECTION ها ) تا موقعی که همزمان چند تا خط یک تابع رو صدا می زنن مشکلی پیش نیاد. ولی مشکل اینه که این توابع من به هم وابسته هستن و مثلا وقتی ی خط داره تابع addNode رو استفاده میکنه ی خط دیگه میاد و DeleteNode رو استفاده می کنه و مشکل پیش میاد.
من می خوام وقتی یکی ازین تابع ها داره کار می کنه بقیه که می خوان توابع دیگه رو استفاده کنن صبر کنه تا کار تابعه تموم بشه .
درواقع هم زمان فقظ یکی ازین توابع باید استفاده بشه .
ی راهش اینه که ی تابع اصلی درست کنم که با یه شرط مشخص بشه کدوم کار رو انجام بده . که به خاطر تفاوت پارامترها و نوع برگشتی متفاوت هر کدومشون خوب در نمیاد .:متفکر:
با مخلوت کردن چند تا while , volatile هم میشه ی کارایی کرد ولی اونم باز خوب نیست .:متفکر:
بهترین راه چیه؟:متفکر:
این شما و این کد ما (بچه ها کد کد بچه ها):لبخند:
Copy Right C UfnCod3r Inc :چشمک::قهقهه:

//////////////////////////////////////////////////////////////////////////Thread Access Control
class TAC
{
CRITICAL_SECTION m_csec;

public:
void init() { ::InitializeCriticalSection(&m_csec); }
void release() { ::DeleteCriticalSection(&m_csec); }
void lock() { ::EnterCriticalSection(&m_csec); }
void unlock() { ::LeaveCriticalSection(&m_csec); }
};

//////////////////////////////////////////////////////////////////////////ForwardList, Thread Safe
template < typename T> class ForwardListTS
{

public:
class Node
{
friend class ForwardListTS;
Node(const T& value) : m_next(nullptr), m_value(value) {}
Node* m_next;
public:
T m_value;
Node* next() const { return m_next; }
};

private:
volatile uintptr m_nodeHead;
Node* m_pLastNode;
mutable TAC m_tac;

public:
ForwardListTS()
{
m_nodeHead = 0;
m_pLastNode = (Node*)&m_nodeHead;
m_tac.init();
}

Node* firstNode() const { return (Node*)m_nodeHead; }
uint nodeCount() const
{
uint n = 0;
Node* iter = firstNode();
m_tac.lock();
while(iter)
{
iter = iter->next();
n++;
}
m_tac.unlock();
return n;
}

Node* push(const T& value)
{
m_tac.lock();
Node* newNode = new Node(value);
m_pLastNode->m_next = newNode;
m_pLastNode = newNode;
m_tac.unlock();
return newNode;
}
bool deleteNode(Node* node)
{
if(node == nullptr) return false;
m_tac.lock();
Node* iter = (Node*)m_nodeHead;
Node* iterPre = (Node*)&m_nodeHead;
while (iter)
{
if(iter == node)
{
if(iter == m_pLastNode)
m_pLastNode = iterPre;
iterPre->m_next = iter->m_next;
delete iter;
return true;
}

iterPre = iter;
iter = iter->next();
}
m_tac.unlock();
return false;
}
Node* operator [] (size_t i) const
{
m_tac.lock();
Node* iter = firstNode();
//while (iter && i) // TEST, JNZ, TEST, JNZ,
while (((uintptr)iter) * i) //avoiding branch MUL, TEST, JNZ
{
iter = iter->next();
i--;
}
m_tac.unlock();
return iter;
}
}

:شیطان::شیطان::شیطان:

omidshaman
سه شنبه 30 مهر 1392, 15:11 عصر
به خاطر اینه که برنامت deadlock داره از recursive mutex می تونی استفاده کنی توی Win32 نمیدونم چه جوریه .
این خط قضیش چیه ؟!!
while (((uintptr)iter) * i)


uintptr چی هست اصلا همون uinptr_t ه؟

UfnCod3r
سه شنبه 30 مهر 1392, 16:04 عصر
این CRITICAL_SECTION ها ی تو وین32 همون recursive mutex هست . میشه چند بار lock رو صدا بزنی .:چشمک:
مشکل اینه که اینا داخل ی تابع رو کنترل می کن. قرض کن مثلا ی خطی داره تابع push رو استفاده می کنه تا ی گره اضافه کنه به لیست . این وسط یه خط دیگه هم تابع clear رو صدا می زنه تا لیست پاک بشه.
و اینجاست که ممکنه مشکل پیش بیاد.
چون این توابع به هم وابسته هستن وقتی یکشون کار می کنه بقیه باید صبر کنن تا کار تموم بشه . افتاد؟:لبخند:
من همه رو تو ی تابع فشرده کردم . اینجا نسبتا جواب داد ولی خب برای جاهای دیگه که نیاز بشه باید ی فکری کنم :متفکر: Event, Semaphore, Mutex, Atomic :متفکر:
:متفکر: البته کاامل تست نشده شاید درست کار نکنه.

template < typename T> class ForwardListTS
{

public:
class Node
{
friend class ForwardListTS;
Node(const T& value) : m_next(nullptr), m_value(value) {}
Node* m_next;
public:
T m_value;
Node* next() const { return m_next; }

inline void* operator new (size_t x) { return gGenericAllocator.alloc(x); }
inline void operator delete (void* x) { gGenericAllocator.free(x); }
};

private:
volatile uintptr m_nodeHead;
Node* m_pLastNode;
mutable TAC m_tac;

public:
ForwardListTS()
{
m_nodeHead = 0;
m_pLastNode = (Node*)&m_nodeHead;
m_tac.init();
}
ForwardListTS(const ForwardListTS& copy)
{
m_tac.init();
m_nodeHead = 0;
m_pLastNode = (Node*)&m_nodeHead;
Node* node = copy.firstNode();
while (node)
{
push(node->m_value);
node = node->next();
}
}
~ForwardListTS()
{
clear();
m_tac.release();
}

ForwardListTS& operator = (const ForwardListTS& copy)
{
clear();
Node* node = copy.firstNode();
while (node)
{
push(node->m_value);
node = node->next();
}
return *this;
}

Node* firstNode() const { return (Node*)m_nodeHead; }

Node* push(const T& value) { return (Node*)_FuncMain(0, (uintptr)&value); }
uint nodeCount() const { return (uint)_FuncMain(2, 0); }
bln deleteNode(Node* node) { return (bln)_FuncMain(3, (uintptr)node); }
void clear() { _FuncMain(4,0); }
Node* operator [] (size_t i) const { return (Node*)_FuncMain(1, (uintptr)i); }







private:
XNOINL uintptr FASTCALL _FuncMain(uintptr i, uintptr data) const
{
typedef ForwardListTS<T> Class;
typedef uintptr (Class::*MemFunc)(uintptr);
static const MemFunc lFuncs[] = { &Class::_Push, &Class::_GetNode, &Class::_NodeCount, &Class::_DeleteNode, &Class::_Clear};

m_tac.lock();
uintptr ret = (((Class*)this)->*(lFuncs[i]))(data);
m_tac.unlock();
return ret;
}

uintptr _Push(uintptr valuePtr)
{
Node* newNode = new Node(*((T*)valuePtr));
m_pLastNode->m_next = newNode;
m_pLastNode = newNode;
return (uintptr)newNode;
}
uintptr _NodeCount(uintptr unused)
{
uintptr n = 0;
Node* iter = firstNode();
while(iter)
{
iter = iter->next();
n++;
}
return n;
}
uintptr _GetNode(uintptr index)
{
Node* iter = firstNode();
while (((uintptr)iter) * index)
{
iter = iter->next();
index--;
}
return (uintptr)iter;
}
uintptr _Clear(uintptr unused)
{
Node* iter = firstNode();
m_nodeHead = 0;
m_pLastNode = (Node*)&m_nodeHead;
while (iter)
{
Node* tmp = iter;
iter = iter->next();
delete tmp;
}
return 0;
}
uintptr _DeleteNode(uintptr nodePtr)
{
const Node* node = (Node*)nodePtr;
if(node == nullptr) return 0;
Node* iter = (Node*)m_nodeHead;
Node* iterPre = (Node*)&m_nodeHead;
while (iter)
{
if(iter == node)
{
if(iter == m_pLastNode)
m_pLastNode = iterPre;
iterPre->m_next = iter->m_next;
delete iter;
return 1;
}

iterPre = iter;
iter = iter->next();
}
return 0;
}
};

عجب چیزی شد :کف:

ن پ ن چی می تونه باشه
اره همون uintptr_t خودمونه . همون size_t خودمون . همون unsigned int خودمون تو 32 بیتی و همون unsigned long long خودمون تو 64 بیتی .:لبخند:
اون خط کار همینو می کنه
while (iter && i)
منتها اپتیمایزیده :لبخند:
تو حالت معمول دو تا TEST انجام می شه و دو تا JNZ .(دو تا مقایسه و 2 تا پرش)
ولی تو حالت بهینه ی ضرب و ی TEST و ی JNZ
:شیطان::شیطان::شیطان::شیطان:: یطان::شیطان::شیطان::شیطان::ش طان::شیطان::شیطان::شیطان::شی ان::شیطان::شیطان::شیطان: