この話は、学生時代や入社当初は全然気づかなくて、会社でプロジェクトを管理したりチームで何かを取り組んだりしていく上で思うようになったことです。
規約や役割を厳しく設けるのは良くないが・・・(ブロッキングな同期I/O)
何かを成し遂げるためには、規約や役割を厳しく設けてしまうと、効率良く物事が進まなくなったりします。これは、I/Oのアーキテクチャに例えると、きちんとブロッキングをして同期しながらI/Oを完了させるようなやり方です。一人のエンジニアが単一のタスクをブロッキングして作業するので、やった所までの成果(ドキュメントやコード)はきちんとでますが、タスクの量が増えたりすると(アクセス集中)、一気にタスクが溜まって作業速度が遅くなってしまします。結果、タスクに増加に比例して、人(スレッド)を追加しなければならなくなります(C10k問題)。Apacheもこういう実装をとっていましたね。
自由にやらせてみるのも良いが・・・(ノンブロッキングな非同期I/O)
そこで、プロジェクトやチームのメンバーの自主性に任せて、自由にやらしてみるという手法が良く取られます。ある意味、イベント駆動型のノンブロッキングな非同期I/Oのような手法です。これはノンブロッキングかつ非同期なので、各自の成果を保障しません。最終的に、成果をチェックしてダメでやり直し(再度I/O)する場合もあるし、成果がきちんと出ていて完了(I/Oが完了)する場合もあります。しかし、ノンブロッキングなため、一人の人(スレッド)が複数のタスクを非同期にこなせるので、タスクの増加にも柔軟に対応(C10k問題の解決)できますし、タスクの量に応じて人を増やす必要もありません(スレッドで複数のリクエストを処理するためスレッドに依存しない)。nginxはこういう実装ですね。
しかし、チームでこれをやろうとすると、最初は大抵の場合失敗します(実装次第でうまくいったりこけたり)。ここには大きな落とし穴があるからです。
自由すぎるが故の問題
ここまで、I/Oに例えて書いてきましたが、結局大事な事は、「人間はスレッドに例えられない」ということです。スレッドは大抵が平等で、全く同じ意識をもって同じ量のタスクをこなせます。一方で、人間は人によってスキルやモチベーションも違うし、何よりも不満やストレスを感じる生き物です。
例えば、何年も共に作業をやってきて、お互いの自主性や能力、そして、アイコンタクトで作業を無意識に分担できるくらいのチームワークがあれば、こういうやり方でもうまくいくと思います。これは、まさに高機能のスケジューラを搭載した、最高のkernelです。しかし、まだ結成間もないチームで、自由にやろうといっても、パレートの法則とまでは言わないですが、チームの中で最も責任感があったり気が回る人が大部分の作業をやっていたりします。
最初は善意でそういう作業をやっているのが、徐々に、「なんで自分だけがこんなにやらないといけないのか」「他の人もやれよ」「前もやれっていったのに」等と、少しずつ不満が溜まっていきます。そして、責任感のある人は、「あー、これそそろやらないといけないな。けど今回は知らぬふりして他の人にやってもらおう」と、自分で気づいているにも関わらず、やらないようにします。そして、それは大抵の場合、いつまでたっても誰もやらず、責任感のある人や気のまわる人にとっては大きなストレスとなって不満になり、いつしか、みんなの代わりにやってあげる事すらやめてしまいます。人間はスレッドのように単純にはできていないのです。
その結果、自由に効率良くやるために規約や役割を設けないようにしたのに、各自の自主性の違いやモチベーションの違いによって、徐々に作業量に差がでてしまい、不満が溜まり、お互いの信頼を失ってチームの雰囲気が悪くなり、結果、ストレスや不満が足かせとなって、誰も自由に動くことができなくなってしまいます。さらには、その矛先が「みんなに指示をしない上司が悪い」と上司やリーダーに向いてしまいます。もはや、メンバー間の信頼、そして、メンバーとリーダーとの信頼は失われ、悪循環を解消することができなくなり、チームは崩壊してしまいます。
どうすればよいか
こういう状況を数年の間に沢山見てきました。これは、個々のモチベーションやスキルに依存し過ぎたために、失敗してしまう典型例だと思います。正に、「人間はスレッドではない」ということです。ですので、自分がこういう状況にならないように、自由な中にも秩序が必要だと考えました。
何をやったかというと、細かい規約や役割は決めませんが、「このタスクが回らなくなった時の責任は誰にあるか」という責任分界点のようなポイントをいくつか決めて、それをメンバーに割り当てるようにしました(親プロセスの生成)。その責任を持つ人(親プロセス)は、そのタスクに関する全ての仕事をするわけではなく、割り振りはみんな(子プロセス)に指示を出して協力してやれるような権限を持ちます。そして、全てのメンバーにそれぞれに少なくとも一つは「あるタスクに関する責任」を持たせます。それによって、それぞれが各仕事に責任を持ちつつ、みんなでやれる環境が徐々に出来上がっていきます。また、曖昧な作業があれば、それをどの責任の範疇にするかを適宜話あって決めていくことで、意味の無い口論やストレスを減らすことができます(プロセス間通信)。これを続けていけば、そもそも「タスクの責任分界点」すら不必要になっていくでしょう(これはOSにはできない事、人間だからこそ可能)。
最も大事な事(スケジューラ)
しかし、これだけれではまだ足りません。最終的に何が必要かというと、「ある仕事に関する責任」をみんながきちんと果たせているかを、第三者の視点で見てあげる人(スケジューラ)が必要になります。それが、プロジェクトのリーダーであり、チームのリーダーなのです。そして、最初の内は、「ある仕事の関する責任」を持った人(親プロセス)に、その配下の全てのタスクのスケジューリングを任せる(ユーザスレッドのスケジューラ)だけではなく、きちんとリーダー自信が全てのタスクのスケジューリングを見てあげる(カーネルスレッドのスケジューラ)のが重要なポイントだと思います。
そして、これが今流行りのLinuxのスケジューリングポリシーとノンブロッキングな非同期I/Oなのです。
「プロジェクトやチームにおいて自由な中にも秩序が必要(スケジューラやI/Oのアーキテクチャで例えてみた)」への1件のフィードバック
コメントは受け付けていません。