Hãy giải thích từ Linux, quan điểm của Windows?
Tôi đang lập trình trong C #, liệu hai thuật ngữ này có làm nên sự khác biệt. Xin vui lòng gửi càng nhiều càng tốt, với các ví dụ và như vậy ....
Cảm ơn
Hãy giải thích từ Linux, quan điểm của Windows?
Tôi đang lập trình trong C #, liệu hai thuật ngữ này có làm nên sự khác biệt. Xin vui lòng gửi càng nhiều càng tốt, với các ví dụ và như vậy ....
Cảm ơn
Câu trả lời:
Đối với Windows, các phần quan trọng có trọng lượng nhẹ hơn so với mutexes.
Mutexes có thể được chia sẻ giữa các tiến trình, nhưng luôn dẫn đến một cuộc gọi hệ thống đến kernel có một số chi phí.
Các phần quan trọng chỉ có thể được sử dụng trong một quy trình, nhưng có lợi thế là chúng chỉ chuyển sang chế độ kernel trong trường hợp tranh chấp - Mua lại không được kiểm soát, nên là trường hợp phổ biến, cực kỳ nhanh. Trong trường hợp tranh chấp, họ nhập kernel để chờ một số nguyên thủy đồng bộ hóa (như một sự kiện hoặc semaphore).
Tôi đã viết một ứng dụng mẫu nhanh so sánh thời gian giữa hai người họ. Trên hệ thống của tôi cho 1.000.000 lượt mua lại và phát hành không được giám sát, một mutex mất hơn một giây. Một phần quan trọng mất ~ 50 ms cho 1.000.000 lượt mua.
Đây là mã kiểm tra, tôi đã chạy mã này và nhận được kết quả tương tự nếu mutex là thứ nhất hoặc thứ hai, vì vậy chúng tôi không thấy bất kỳ hiệu ứng nào khác.
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;
// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
}
QueryPerformanceCounter(&end);
int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
}
QueryPerformanceCounter(&end);
int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS);
Từ góc độ lý thuyết, một phần quan trọng là một đoạn mã không được chạy bởi nhiều luồng cùng một lúc vì mã truy cập các tài nguyên được chia sẻ.
Một mutex là một thuật toán (và đôi khi là tên của cấu trúc dữ liệu) được sử dụng để bảo vệ các phần quan trọng.
Semaphores và Màn hình là những triển khai phổ biến của một mutex.
Trong thực tế có nhiều triển khai mutex có sẵn trong các cửa sổ. Chúng chủ yếu khác nhau do hậu quả của việc thực hiện bởi mức độ khóa, phạm vi, chi phí và hiệu suất của chúng dưới các mức độ tranh chấp khác nhau. Xem CLR Inside Out - Sử dụng đồng thời cho khả năng mở rộng cho biểu đồ chi phí của các triển khai mutex khác nhau.
Nguyên thủy đồng bộ hóa có sẵn.
Câu lock(object)
lệnh được triển khai bằng cách sử dụng Monitor
- xem MSDN để tham khảo.
Trong những năm qua, nhiều nghiên cứu được thực hiện về đồng bộ hóa không chặn . Mục tiêu là để thực hiện các thuật toán theo cách không khóa hoặc chờ miễn phí. Trong các thuật toán như vậy, một quy trình giúp các quy trình khác hoàn thành công việc của họ để quy trình cuối cùng có thể hoàn thành công việc của mình. Do đó, một quy trình có thể hoàn thành công việc của nó ngay cả khi các quy trình khác, đã cố gắng thực hiện một số công việc, treo. Usinig khóa, họ sẽ không phát hành khóa của họ và ngăn chặn các quá trình khác tiếp tục.
Ngoài các câu trả lời khác, các chi tiết sau đây dành riêng cho các phần quan trọng trên windows:
InterlockedCompareExchange
hoạt độngTrong linux, tôi nghĩ rằng họ có một "khóa spin" phục vụ mục đích tương tự như phần quan trọng với số lần quay.
Phần quan trọng và Mutex không phải là hệ điều hành cụ thể, các khái niệm của chúng về đa luồng / đa xử lý.
Phần quan trọng Là một đoạn mã chỉ phải tự chạy ở bất kỳ thời điểm nào (ví dụ: có 5 luồng chạy đồng thời và một hàm gọi là "then_section_feft" cập nhật một mảng ... bạn không muốn có tất cả 5 luồng cập nhật mảng cùng một lúc. Vì vậy, khi chương trình đang chạy tới_s_s_s_bức (), không có luồng nào trong số các luồng khác phải chạy tới tệp_s_s_số_fection của chúng.
mutex * Mutex là một cách để triển khai mã phần quan trọng (nghĩ về nó giống như một mã thông báo ... chủ đề phải có quyền sở hữu nó để chạy tới phần phê bình)
Một mutex là một đối tượng mà một luồng có thể có được, ngăn chặn các luồng khác có được nó. Đó là tư vấn, không bắt buộc; một luồng có thể sử dụng tài nguyên mà mutex đại diện mà không cần lấy nó.
Phần quan trọng là độ dài mã được đảm bảo bởi hệ điều hành để không bị can thiệp. Trong mã giả, nó sẽ giống như:
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
Windows 'nhanh' bằng với lựa chọn quan trọng trong Linux sẽ là một Futex , viết tắt của mutex không gian người dùng nhanh. Sự khác biệt giữa Futex và mutex là với Futex, kernel chỉ tham gia khi cần phân xử trọng tài, vì vậy bạn tiết kiệm chi phí nói chuyện với kernel mỗi khi bộ đếm nguyên tử được sửa đổi. Điều đó .. có thể tiết kiệm một lượng đáng kể thời gian đàm phán khóa trong một số ứng dụng.
Một Futex cũng có thể được chia sẻ giữa các quy trình, sử dụng các phương tiện bạn sẽ sử dụng để chia sẻ một mutex.
Thật không may, Futexes có thể rất khó thực hiện (PDF). (Cập nhật năm 2018, chúng không đáng sợ như năm 2009).
Ngoài ra, nó khá giống nhau trên cả hai nền tảng. Bạn đang tạo các bản cập nhật nguyên tử, mã thông báo cho một cấu trúc được chia sẻ theo cách mà (hy vọng) không gây ra chết đói. Những gì còn lại chỉ đơn giản là phương pháp để thực hiện điều đó.
Chỉ cần thêm 2 xu của tôi, các Phần quan trọng được xác định là cấu trúc và các thao tác trên chúng được thực hiện trong ngữ cảnh chế độ người dùng.
ntdll! _RTL_CRITICS_SECTION + 0x000 DebugInfo: Ptr32 _RTL_CRITICAL_SECTION_DEBUG + Khóa 0x004: Int4B + Đệ quy 0x008: Int4B + 0x00c Sở hữuThread: Ptr32 Void + 0x010 LockSemaphore: Vô hiệu Ptr32 + Số lần quay 0x014: Uint4B
Trong đó mutex là các đối tượng kernel (ExMutantObjectType) được tạo trong thư mục đối tượng Windows. Hoạt động Mutex chủ yếu được thực hiện trong chế độ kernel. Chẳng hạn, khi tạo Mutex, cuối cùng bạn gọi nt! NtCreateMutant trong kernel.
Câu trả lời tuyệt vời từ Michael. Tôi đã thêm một thử nghiệm thứ ba cho lớp mutex được giới thiệu trong C ++ 11. Kết quả là hơi thú vị và vẫn hỗ trợ chứng thực ban đầu của anh ấy về các đối tượng CRITICAL_SECTION cho các quy trình đơn lẻ.
mutex m;
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;
// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
}
QueryPerformanceCounter(&end);
int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);
}
QueryPerformanceCounter(&end);
int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
// Force code into memory, so we don't see any effects of paging.
m.lock();
m.unlock();
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
m.lock();
m.unlock();
}
QueryPerformanceCounter(&end);
int totalTimeM = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);
printf("C++ Mutex: %d Mutex: %d CritSec: %d\n", totalTimeM, totalTime, totalTimeCS);
Kết quả của tôi là 217, 473 và 19 (lưu ý rằng tỷ lệ số lần của tôi trong hai lần gần nhất tương đương với Michael, nhưng máy của tôi trẻ hơn anh ấy ít nhất bốn tuổi, vì vậy bạn có thể thấy bằng chứng về tốc độ tăng từ năm 2009 đến 2013 , khi XPS-8700 ra đời). Lớp mutex mới nhanh gấp đôi so với mutex Windows, nhưng vẫn chưa bằng một phần mười tốc độ của đối tượng Windows CRITICAL_SECTION. Lưu ý rằng tôi chỉ thử nghiệm mutex không đệ quy. Các đối tượng CRITICS_SECTION được đệ quy (một luồng có thể nhập chúng liên tục, miễn là nó để lại cùng một số lần).
Các hàm AC được gọi là reentrant nếu nó chỉ sử dụng các tham số thực tế của nó.
Các hàm reentrant có thể được gọi bởi nhiều luồng cùng một lúc.
Ví dụ về hàm reentrant:
int reentrant_function (int a, int b)
{
int c;
c = a + b;
return c;
}
Ví dụ về hàm không reentrant:
int result;
void non_reentrant_function (int a, int b)
{
int c;
c = a + b;
result = c;
}
Thư viện tiêu chuẩn C strtok () không được phát hành lại và không thể được sử dụng bởi 2 hoặc nhiều luồng cùng một lúc.
Một số SDK nền tảng đi kèm với phiên bản reentrant của strtok () được gọi là strtok_r ();
Enrico Migliore