Во-первых, у меня проблема с Java 11 (OpenJdk, Debian).
Все, что мне нужно, это просто прервать поток после 20-миллисекундного срока. Например, как в клиентской библиотеке Google GRP C java. Таким образом, обычный способ добиться этого - создать ScheduledThreadPoolExecutor и отправить задачу Watchdog с задержкой (в моем случае 20 мс). После этого нам просто нужно отправить прерывание в рабочий поток, который ожидает. Конечно, потоки сторожевого таймера должны иметь максимальный приоритет.
Поток сторожевого таймера срабатывает почти точно через 20 мс в случае низкой загрузки системы. И это ожидается. Но в случае 100% загрузки системы другими потоками (количество потоков совпадает с номером ядра ЦП или в два раза больше) ScheduledThreadPoolExecutor.schedule с задержкой 20 мс может просыпаться иногда через 25 мс или 40 и более и более миллисекунд!
Как мой единственный поток с наивысшим приоритетом не может получить процессорное время более 40 мс?
Я пробовал это на своем ноутбуке с ядром i7 6 с ядром HT (12 логических) / nix 5.3.0- 40-generi c # 32 ~ 18.04.1-Ubuntu SMP
После этого я написал код на C ++ для проверки точности сна и пробуждения потока с высоким приоритетом, и это происходит снова! Поток сторожевого таймера просто спит в течение 1 мс (требуется проверить состояние 1000 раз в секунду). Поведение несколько сумасшедшее.
Более того - на ноутбуке моего друга все работает нормально с более чем 100 рабочими потоками, показывающими всегда задержку менее 10 с нами. Программа работает под sudo, чтобы установить приоритет потока.
g ++ -pthread -o темы потоков. cpp
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <chrono>
#include <cstring>
#include <pthread.h>
void watchdog() {
long i = 9000000000;
auto last = std::chrono::high_resolution_clock::now();
int sleep_duration_us = 1000;
while (i-- > 0) {
std::this_thread::sleep_for (std::chrono::microseconds(sleep_duration_us));
auto now = std::chrono::high_resolution_clock::now();
auto delay = std::chrono::duration_cast<std::chrono::microseconds>(now-last).count() - sleep_duration_us;
if (i % 100 == 0)
std::cout << "Awakening delay: " << delay << " us\n";
if (delay > 1000)
std::cerr << "LONG DELAY!!! " << delay << " us" << std::endl;
// if (sleep_time > 1000000) {
// std::cout << "\n watchdog fail - " << sleep_time ;
// }
// if (i % 10000000 == 0)
// std::cout << "\n watchdog - " << std::chrono::duration_cast<std::chrono::nanoseconds>(now-last).count() ;
last = now;
}
}
int main()
{
// vector container stores threads
std::vector<std::thread> workers;
for (int i = 0; i < 10; i++) {
auto t = std::thread([]()
{
int i = 0, j = i;
while (true) {
if (j % 2 == 0)
j /= 2;
else
j = 3 * j + 1;
if (j == 123456789)
std::cout << j << std::endl;
}
});
workers.push_back(std::move(t));
}
std::cout << "main thread\n";
std::thread tw(watchdog);
sched_param sch;
int policy;
pthread_getschedparam(tw.native_handle(), &policy, &sch);
std::cout << "default priority " << sch.sched_priority << std::endl;
sch.sched_priority = 99;
if(pthread_setschedparam(tw.native_handle(), SCHED_RR, &sch)) {
std::cout << "Failed to setschedparam: " << std::strerror(errno) << '\n';
}
pthread_getschedparam(tw.native_handle(), &policy, &sch);
std::cout << "new priority " << sch.sched_priority << std::endl;
std::for_each(workers.begin(), workers.end(), [](std::thread &t)
{
std::cout << t.get_id() << "\n";
});
std::this_thread::sleep_for(std::chrono::seconds(15));
std::cout << "finish\n";
return 0;
}
Мой вывод:
root@openminder-nix:/source/scheduler_test# sudo ./threads
main thread
default priority 0
new priority 99
140018767865600
140018759472896
140018751080192
140018742687488
140018734294784
140018725902080
140018717509376
140018709116672
140018700723968
140018692331264
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 3 us
Awakening delay: 4 us
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 2 us
Awakening delay: 3 us
Awakening delay: 2 us
Awakening delay: 3 us
Awakening delay: 2 us
LONG DELAY!!! 2048 us
Awakening delay: 7 us
Awakening delay: 4 us
Awakening delay: 5 us
LONG DELAY!!! 2199 us
Awakening delay: 5 us
Awakening delay: 4 us
Awakening delay: 6 us
Awakening delay: 5 us
Awakening delay: 8 us
Awakening delay: 5 us
Awakening delay: 5 us
Awakening delay: 4 us
Awakening delay: 6 us
LONG DELAY!!! 1195 us
Поэтому, пожалуйста, помогите мне понять механизм планирования потоков linux и написать код с точностью сторожевого таймера в 1 мс.