diff --git a/NEWS.md b/NEWS.md index 48c35498e834a..f9de3c46fb87f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -207,7 +207,7 @@ Deprecated or removed functions (`airyai`, `airybi`, `airyaiprime`, `airybiprimex`, `airyaix`, `airybix`, `airyaiprimex`, `airybiprimex`) ([#18050]). - * `produce`, `consume` and iteration over a Task object has been deprecated in favor of + * `produce`, `consume` and iteration over a Task object have been deprecated in favor of using Channels for inter-task communication ([#19841]). Julia v0.5.0 Release Notes diff --git a/base/channels.jl b/base/channels.jl index 94eec5d43c731..5b6266674da76 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -59,7 +59,7 @@ Channel(sz) = Channel{Any}(sz) Channel(func::Function; ctype=Any, csize=0, taskref=nothing) Creates a new task from `func`, binds it to a new channel of type -`ctype` and size `csize`, schedules the task, all in a single call. +`ctype` and size `csize`, and schedules the task, all in a single call. `func` must accept the bound channel as its only argument. @@ -71,8 +71,8 @@ Returns a Channel. ```jldoctest julia> chnl = Channel(c->foreach(i->put!(c,i), 1:4)); -julia> @show typeof(chnl); -typeof(chnl) = Channel{Any} +julia> typeof(chnl) +Channel{Any} julia> for i in chnl @show i @@ -91,16 +91,14 @@ julia> taskref = Ref{Task}(); julia> chnl = Channel(c->(@show take!(c)); taskref=taskref); -julia> task = taskref[]; - -julia> @show istaskdone(task); -istaskdone(task) = false +julia> istaskdone(taskref[]) +false julia> put!(chnl, "Hello"); take!(c) = "Hello" -julia> @show istaskdone(task); -istaskdone(task) = true +julia> istaskdone(taskref[]) +true ``` """ @@ -111,7 +109,7 @@ function Channel(func::Function; ctype=Any, csize=0, taskref=nothing) schedule(task) yield() - isa(taskref, Ref{Task}) && (taskref.x = task) + isa(taskref, Ref{Task}) && (taskref[] = task) return chnl end @@ -158,7 +156,7 @@ Terminating tasks have no effect on already closed Channel objects. When a channel is bound to multiple tasks, the first task to terminate will close the channel. When multiple channels are bound to the same task, -termination of the task will close all channels. +termination of the task will close all of the bound channels. ```jldoctest julia> c = Channel(0); @@ -175,8 +173,8 @@ i = 2 i = 3 i = 4 -julia> @show isopen(c); -isopen(c) = false +julia> isopen(c) +false ``` @@ -187,7 +185,8 @@ julia> task = @schedule (put!(c,1);error("foo")); julia> bind(c,task); -julia> take!(c); +julia> take!(c) +1 julia> put!(c,1); ERROR: foo @@ -223,7 +222,7 @@ function channeled_tasks(n::Int, funcs...; ctypes=fill(Any,n), csizes=fill(0,n)) # bind all tasks to all channels and schedule them foreach(t -> foreach(c -> bind(c,t), chnls), tasks) - foreach(t->schedule(t), tasks) + foreach(schedule, tasks) yield() # Allow scheduled tasks to run diff --git a/base/deprecated.jl b/base/deprecated.jl index 4d2a0f5e8e98f..e80f7574373ea 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1625,6 +1625,7 @@ function produce(v) end end produce(v...) = produce(v) +export produce function consume(P::Task, values...) depwarn("consume is now deprecated. Use Channels for inter-task communication.", :consume) @@ -1655,6 +1656,7 @@ function consume(P::Task, values...) P.state == :runnable ? schedule_and_wait(P) : wait() # don't attempt to queue it twice end +export consume function start(t::Task) depwarn(string("Task iteration is now deprecated.", diff --git a/base/exports.jl b/base/exports.jl index 8a91691864c68..b15d7aa874ea8 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -954,14 +954,12 @@ export # tasks and conditions Condition, - consume, current_task, islocked, istaskdone, istaskstarted, lock, notify, - produce, ReentrantLock, schedule, task_local_storage, diff --git a/doc/src/manual/control-flow.md b/doc/src/manual/control-flow.md index 227018a13098c..42d63743c71ab 100644 --- a/doc/src/manual/control-flow.md +++ b/doc/src/manual/control-flow.md @@ -846,8 +846,11 @@ Julia provides a [`Channel`](@ref) mechanism for solving this problem. A [`Channel`](@ref) is a waitable FIFO queue which can have multiple tasks reading and writing to it. Let's define a producer task, which produces values via the [`put!`](@ref) call. +To consume values, we need to schedule the producer to run in a new task. A special [`Channel`](@ref) +constructor which accepts a 1-arg function as an argument can be used to run a task bound to a channel. +We can then [`take!()`](@ref) values repeatedly from the channel object: -```julia +```jldoctest julia> function producer(c::Channel) put!(c, "start") for n=1:4 @@ -855,13 +858,7 @@ julia> function producer(c::Channel) end put!(c, "stop") end; -``` -To consume values, we need to schedule the producer to run in a new task. A special [`Channel`](@ref) -constructor which accepts a 1-arg function as an argument can be used to run a task bound to a channel. -We can then [`take!()`](@ref) values repeatedly from the channel object: - -```jldoctest julia> chnl = Channel(producer); julia> take!(chnl) @@ -901,7 +898,7 @@ start stop ``` -Note that we did not have to explcitly close the channel in the producer. This is because +Note that we did not have to explicitly close the channel in the producer. This is because the act of binding a [`Channel`](@ref) to a [`Task()`](@ref) associates the open lifetime of a channel with that of the bound task. The channel object is closed automatically when the task terminates. Multiple channels can be bound to a task, and vice-versa. @@ -944,7 +941,7 @@ this might be. If you switch away from the current task, you will probably want to it at some point, but knowing when to switch back, and knowing which task has the responsibility of switching back, can require considerable coordination. For example, [`put!()`](@ref) and [`take!()`](@ref) are blocking operations, which, when used in the context of channels maintain state to remember -who the consumers is. Not needing to manually keep track of the consuming task is what makes [`put!()`](@ref) +who the consumers are. Not needing to manually keep track of the consuming task is what makes [`put!()`](@ref) easier to use than the low-level [`yieldto()`](@ref). In addition to [`yieldto()`](@ref), a few other basic functions are needed to use tasks effectively. diff --git a/test/channels.jl b/test/channels.jl index 376086d5fd534..d3260f57920a1 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -115,13 +115,13 @@ for N in [0,10] @test_throws ErrorException fetch(cs[i]) end - # Multiple tasks, first one to terminate, closes the channel + # Multiple tasks, first one to terminate closes the channel nth = rand(1:5) ref = Ref(0) cond = Condition() tf3(i) = begin if i == nth - ref.x = i + ref[] = i else sleep(2.0) end @@ -130,10 +130,10 @@ for N in [0,10] tasks = [Task(()->tf3(i)) for i in 1:5] c = Channel(N) foreach(t->bind(c,t), tasks) - foreach(t->schedule(t), tasks) + foreach(schedule, tasks) @test_throws InvalidStateException wait(c) @test !isopen(c) - @test ref.x == nth + @test ref[] == nth # channeled_tasks for T in [Any, Int]