إطار عمل future
استخدم plan(multisession) وfuture() للتوازي غير المتزامن والقابل للنقل
إطار عمل future درس مجاني في R Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في R Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة R Academy 4 دروس في المجموع.
ما هي حزمة future؟
توفر حزمة future واجهة برمجية موحّدة وعالية المستوى للبرمجة غير المتزامنة والمتوازية في R. ويمثّل future قيمةً مؤقتة ستصبح متاحة لاحقًا، وقد تُحسَب على نواة أخرى أو جهاز آخر.
library(future)
# A simple future: computation happens asynchronously
f <- future({
Sys.sleep(0.5) # simulate slow work
42
})
cat('Future created, doing other work...\n')
# Retrieve the result (blocks until done)
result <- value(f)
cat('Result:', result, '\n')plan(): اختيار الواجهة الخلفية
تحدّد plan() استراتيجية التنفيذ لجميع القيم المستقبلية اللاحقة. ومن أكثر الاستراتيجيات شيوعًا sequential (الافتراضية، بخيط تنفيذ واحد)، وmultisession (جلسات R متعددة، وتعمل في كل البيئات)، وmulticore (التفرّع، ويعمل في Unix وmacOS فقط).
library(future)
# Default: sequential (no parallelism)
plan(sequential)
cat('Strategy:', class(plan())[1], '\n')
# Parallel with separate R sessions (works on Windows too)
plan(multisession, workers = 4)
cat('Strategy:', class(plan())[1], '\n')
# Forking (Unix/macOS only, lower overhead)
if (.Platform$OS.type != 'windows') {
plan(multicore, workers = 4)
cat('Strategy:', class(plan())[1], '\n')
}
# Reset to sequential
plan(sequential)value(): الانتظار المتزامن للنتائج
تحظر value(f) العملية الحالية حتى يكتمل حساب القيمة المستقبلية، ثم تُرجع النتيجة. وإذا أطلقت القيمة المستقبلية خطأً، فإن value() تعيد إطلاقه في جلسة الاستدعاء.
library(future)
plan(multisession, workers = 2)
# Launch two slow tasks concurrently
f1 <- future({ Sys.sleep(0.3); sum(1:1000) })
f2 <- future({ Sys.sleep(0.3); prod(1:10) })
# Both run in parallel; total time ~ 0.3s not 0.6s
start <- proc.time()[['elapsed']]
v1 <- value(f1)
v2 <- value(f2)
elapsed <- proc.time()[['elapsed']] - start
cat('v1:', v1, ' v2:', v2, '\n')
cat('Elapsed:', round(elapsed, 2), 's\n')
plan(sequential)المعامل %<-%
المعامل %<-% هو صياغة مختصرة تجمع بين future() وvalue() الكسول. ويبدو كإسناد عادي، لكن الطرف الأيمن يُنفَّذ بشكل غير متزامن. وتُسترجع القيمة عند أول وصول إلى المتغير.
library(future)
plan(multisession, workers = 2)
# %<-% starts the computation immediately in the background
x %<-% {
Sys.sleep(0.3)
rnorm(5, mean = 10)
}
y %<-% {
Sys.sleep(0.3)
runif(5, min = 1, max = 5)
}
cat('Both running in background...\n')
# Accessing x or y here blocks until ready
cat('x:', round(x, 2), '\n')
cat('y:', round(y, 2), '\n')
plan(sequential)الاكتشاف التلقائي للمتغيرات العامة
من أقوى ميزات future اكتشاف المتغيرات العامة: إذ يعثر تلقائيًا على المتغيرات التي تشير إليها شيفرة القيمة المستقبلية في بيئة الاستدعاء، ثم يرسلها إلى العامل، من دون الحاجة إلى clusterExport() يدويًا.
library(future)
plan(multisession, workers = 2)
# These globals are auto-detected and sent to the worker
threshold <- 50
multiplier <- 3
f <- future({
x <- threshold * multiplier # uses both globals
x + 1
})
cat('Result:', value(f), '\n') # 151
# Inspect which globals were identified
future_obj <- future({
threshold + multiplier
}, lazy = TRUE)
cat('Globals found:', paste(names(future::getGlobalsAndPackages(future_obj)$globals), collapse = ', '), '\n')
plan(sequential)future_options: ضبط السلوك
تتحكم إعدادات future.options (التي يمكن الوصول إليها عبر وسيطات options() أو future::plan()) في الإعدادات العامة، مثل الحد الأقصى للذاكرة المسموح بها لكل قيمة مستقبلية، واستراتيجية اكتشاف المتغيرات العامة، وسلوك المهلة الزمنية.
library(future)
# Cap memory per future at 500 MB
options(future.globals.maxSize = 500 * 1024^2) # bytes
# Disable automatic global detection (manual control)
options(future.globals.onReference = 'error')
# Inspect current settings
cat('Max globals size (MB):',
getOption('future.globals.maxSize') / 1024^2, '\n')
# Restore defaults
options(future.globals.maxSize = 500 * 1024^2)
options(future.globals.onReference = NULL)
plan(multisession, workers = 2)
# Large object: you'd get an error if it exceeds the cap
big_vec <- 1:1e5 # small enough
f <- future(sum(big_vec))
cat('Sum:', value(f), '\n')
plan(sequential)القيم المستقبلية المتداخلة
يمكن تداخل القيم المستقبلية؛ إذ يمكن لقيمة مستقبلية أن تنشئ قيمًا مستقبلية داخلية. وبشكل افتراضي، تُنفَّذ القيم الداخلية بالتتابع، ما لم تضبط خطة متداخلة باستخدام plan(list(...)). ويفيد ذلك في تحقيق التوازي على مستويين.
library(future)
# Two-level parallelism: outer uses multisession, inner sequential
plan(list(multisession, sequential))
outer_futures <- lapply(1:3, function(i) {
future({
# Each outer worker runs its own sequential inner work
inner <- lapply(1:4, function(j) i * j)
unlist(inner)
})
})
results <- lapply(outer_futures, value)
for (i in seq_along(results)) {
cat('Outer', i, ':', results[[i]], '\n')
}
plan(sequential)معالجة الأخطاء باستخدام القيم المستقبلية
عندما يطلق حساب قيمة مستقبلية خطأً، تعيد value() إطلاقه. ويمكن إحاطة value() بـ tryCatch() لمعالجة أخطاء القيم المستقبلية الفردية من دون إيقاف مسار المعالجة بأكمله.
library(future)
plan(multisession, workers = 2)
# This future will error
bad_future <- future(log(-1, base = 'oops')) # invalid arg
good_future <- future(sqrt(144))
# Handle errors gracefully
bad_result <- tryCatch(
value(bad_future),
error = function(e) {
cat('Caught error:', conditionMessage(e), '\n')
NA
}
)
good_result <- value(good_future)
cat('bad_result:', bad_result, '\n')
cat('good_result:', good_result, '\n')
plan(sequential)التحقق من حالة القيمة المستقبلية
تعيد resolved(f) القيمة TRUE إذا اكتملت القيمة المستقبلية، من دون حظر التنفيذ. ويتيح لك ذلك التحقق دوريًا من اكتمالها وإنجاز عمل مفيد أثناء الانتظار، وإنشاء حلقة تحقق غير حاجزة.
library(future)
plan(multisession, workers = 2)
f <- future({
Sys.sleep(0.5)
'done'
})
# Poll without blocking
counter <- 0
while (!resolved(f)) {
counter <- counter + 1
Sys.sleep(0.1)
cat('Polling... (', counter, ')\n')
}
cat('Future resolved! Result:', value(f), '\n')
cat('Polled', counter, 'times\n')
plan(sequential)دمج future مع lapply
ادمج lapply() مع future() لتوزيع N من المهام، ثم استخدم lapply() مع value() لجمع النتائج. يمنحك هذا النمط تحكمًا صريحًا في وقت بدء كل قيمة مستقبلية.
library(future)
plan(multisession, workers = 4)
# Fan out: create all futures
params <- list(
list(n = 1000, mean = 0),
list(n = 1000, mean = 5),
list(n = 1000, mean = 10),
list(n = 1000, mean = -3)
)
futures <- lapply(params, function(p) {
future({
x <- rnorm(p$n, mean = p$mean)
list(mean = mean(x), sd = sd(x))
})
})
# Fan in: collect results
results <- lapply(futures, value)
for (i in seq_along(results)) {
cat('Param mean', params[[i]]$mean,
'-> observed mean:', round(results[[i]]$mean, 3), '\n')
}
plan(sequential)حالة استخدام واقعية: تقييم طيات التحقق المتقاطع
التحقق المتقاطع مسألة قابلة للتوازي بسهولة، لأن كل طية مستقلة عن الأخرى. وباستخدام القيم المستقبلية، يمكنك تقييم جميع الطيات K في الوقت نفسه، مما يقلل بدرجة كبيرة الزمن الفعلي اللازم للنماذج المكلفة.
library(future)
plan(multisession, workers = 4)
set.seed(42)
data <- data.frame(x = rnorm(200), y = rnorm(200))
fold_ids <- sample(rep(1:4, 50))
# Launch all folds in parallel
fold_futures <- lapply(1:4, function(k) {
future({
train <- data[fold_ids != k, ]
test <- data[fold_ids == k, ]
fit <- lm(y ~ x, data = train)
preds <- predict(fit, newdata = test)
rmse <- sqrt(mean((test$y - preds)^2))
rmse
})
})
rmses <- unlist(lapply(fold_futures, value))
cat('Fold RMSEs:', round(rmses, 4), '\n')
cat('Mean RMSE:', round(mean(rmses), 4), '\n')
plan(sequential)اختبار سريع
تريد تشغيل شيفرة متوازية تعمل على كل من Windows وmacOS. أي استراتيجية plan() ينبغي أن تختار؟
مراجعة: إطار future
أهم النقاط:
- تشغّل
future({expr})تعبيرًا بشكل غير متزامن، بينما تسترجعvalue(f)النتيجة -
%<-%صياغة مختصرة؛ فهي إسناد مع تقييم كسول للقيمة المستقبلية - استخدم
plan(multisession)للتوافق بين الأنظمة، وplan(multicore)للتفرّع في Unix وmacOS - يُكتشف المتغيرات العامة تلقائيًا وتُرسل إلى العاملين، من دون الحاجة إلى
clusterExport() - تتحقق
resolved(f)من الاكتمال من دون حظر التنفيذ - أحط
value()بـtryCatch()لإنشاء مسارات معالجة متحملة للأخطاء - استدعِ دائمًا
plan(sequential)أوplan()لإعادة الضبط عند الانتهاء
library(future)
plan(multisession, workers = 2)
# Concise pattern: fan-out then fan-in
task <- function(i) future({ i^2 + i })
results <- lapply(1:6, task)
cat(unlist(lapply(results, value)), '\n') # 2 6 12 20 30 42
plan(sequential)الأسئلة الشائعة
هل درس «إطار عمل future» مجاني؟
نعم — نص درس «إطار عمل future» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة R Academy، انتقل إلى CoddyKit PRO. تتضمن دورة R Academy 4 دروس في المجموع.
ماذا ستتعلم في «إطار عمل future»؟
استخدم plan(multisession) وfuture() للتوازي غير المتزامن والقابل للنقل تتمرن على R Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ R Academy؟
لا تُشترط خبرة سابقة. R Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «إطار عمل future»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس R Academy هذا؟
نعم. كل درس في R Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- حزمة parallel وdetectCores()
- إطار عمل future
- furrr: عمليات purrr المتوازية
- تصحيح أخطاء التعليمات البرمجية المتوازية وموازنة الأحمال