一个典型的生产者和消费者的题目和实现

前几天一个朋友问到Java为什么线程间不能修改对方的成员变量,自己写了个测试用例,发现没有问题,觉得很奇怪。然后便问了题目,倒是觉得挺有意思的,可以作为笔试题,考察多线程和消息队列。

附件是我写的实现。


==========================================================================


任务:

 

有n 个领舞和m个follower在一个舞蹈学校。 每一个舞者有一个记录卡写有他和谁跳了哪只舞。分别有以下8种形式:

1.  Waltz

2.  Tango

3.  Foxtrot

4.  Quickstep

5.  Rumba

6.  Samba

7.  Cha Cha

8.  Jive

设想每个人都想跳每只舞,但他们不能和同一个舞伴跳超过两支舞。领舞邀请一个follower选择他们想跳哪只舞, 与此同时不能邀请其他人,直到他知道他正在邀请的人是拒绝还是接受他。 如果接受,他们开始邀请其他的人完成他们记录卡上的其他舞。Follower等待邀请,并接受领舞的邀请(可能情况1:如果他们没有和任何领舞跳过那种舞,即可以接受。情况2:他们如果已经接受和该领舞跳另外两种舞了,则不能接受)

 

l   每个领舞如果在以上8种形式的舞都找到了舞伴,即停止。

l   当所有领舞停止寻找时,配对过程结束,最终结果反馈出来。(不必要完美的配对,每个领舞都刚好能在每个舞种上找到舞伴,但当程序停止时,将不再有可能的变动)

 

规则:

l   有n 个领舞和m个follower

l   每个人只有一次机会和另外一个人跳同一种舞(例如:你不能跳两遍Waltz)

l   领舞邀请follower 的具体实现方法不限(例如可能的有:随机选择,按次序选择,从他们自己的号码相同的follower开始依次选等等不限)

l   领舞必须等待正在邀请的人回复以后才能邀请下一个人。注意:领舞可能在一下这种情况下邀请失败 e.g.,如果你尝试将一个值放进一个 buffer 但被告知没放入, 你可以邀请其他人来跳舞. (Some libraries will offer non-blocking put functions that return a boolean: true for "yes, you successfully put the value in the buffer" or false for "no, there was no room in the buffer, and I didn't wait around for things to change". If you are only using functionality that definitely blocks when attempting to put into a full buffer, you may indeed have all your leaders queued up, waiting to ask some particular follower for a dance!).

l   如果领舞没有人可以邀请了,此时领舞停止。

l   follower判断是否接受跳舞的情况(可能情况1:如果他们没有跳过那种舞,即可以接受。情况2:他们如果已经接受和该领舞跳另外两种舞了,则不能接受)

 

 

 

 

要求:

l   每一个人是一个独立的thread

l   另一个“main” thread 应该用来打印最后的结果

l   每一个人有一个资源库(类似于一个buffer)可以用来接收别人的消息,并且只有本人可以阅读自己的资源库,且每次只能处理一条消息(these communication resources must all be collectively accessible at the same time as each other, but individually accessible by only one person at a time.)

l   由命令行输入需要的两个整型变量,先n后m

l   领舞会被编号1到n, follower 会被编号1-m

l   输出结果如下(“——”表示没有配到舞伴)

 

Leader 1:

Waltz      with 3

Tango      with 5

Foxtrot    with 2

Quickstep  with 1

Rumba      ------

Samba      with 2

Cha Cha    with 3

Jive       ------

 

Leader 2:

Waltz      with 2

Tango      with 4

...

 

不用使每一个领舞在每一个舞种上找到了舞伴(完美配对),有时可能会产生配不到的情况。而且n 和m也可能是两个不同的数


===============================================================================================================

Task

You and your partner will implement the following concurrent program in an imperative language (either C pthreads or Java threads, or please pre-approve your choice). Next, you'll implement the program in Haskell using concurrent mechanisms. Lastly, you will write up the experience with any relevant details, things you tried that was surprising or difficult or informative, and any build-instructions needed to run your code.

There are N leaders and M followers at the Socrates School of Dance and Deep Thought. Each dancer has a dance card that should be filled out by listing the person with whom they will dance that song. It has the following eight entries on it:

9.  Waltz

10.              Tango

11.              Foxtrot

12.              Quickstep

13.              Rumba

14.              Samba

15.              Cha Cha

16.              Jive

Everybody wants to dance each of the dances, but they cannot dance more than two dances with the same partner - it would be impolite. Leaders invite any follower of their choosing to dance a specific dance, and cannot ask anyone else to dance until they are either accepted or rejected. If accepted, they start asking anyone for other available dances on their own dance card. Followers wait for invitations, and then accept a dance if they aren't already dancing that one (or haven't already agreed to two dances with that leader).

•   A leader with a full dance card also stops searching for partners.

•   When all leaders have stopped searching for partners, the matching process is over and results should be reported. It is not necessary to achieve perfect matching, but there must be no more possible moves when your program stops.

Rules

•   There will be N leaders and M followers.

•   One dance of each style will play, for a total of eight songs; this means that every individual has one chance at dancing with someone during each song (e.g., you can't dance two+ waltzes).

•   leaders employ any strategy you can think of to ask any follower to dance with them for any particular dance.

◦                             some possibilities: random selection, sequential, beginning with their own number (it's okay for them to know they are leader #5 of 8, for example), or any alternative of your own devising.

•   leaders must wait on a response before they can ask anyone else. Note, though, that implementation-wise, you may find that a leader attempted to ask a follower but the attempt failed in some sense - e.g., if you tried to put a value into a buffer but were told it didn't happen, then you are free to instead go ask someone else to dance. (Some libraries will offer non-blocking put functions that return a boolean: true for "yes, you successfully put the value in the buffer" or false for "no, there was no room in the buffer, and I didn't wait around for things to change". If you are only using functionality that definitely blocks when attempting to put into a full buffer, you may indeed have all your leaders queued up, waiting to ask some particular follower for a dance!).

•   when there is nobody else a leader can ask to dance for any dances, the leader is done attempting to fill out their dance card.

•   followers wait for invitations, and then respond "yes" if they don't yet have a dance partner for the dance, and if they haven't already agreed to dance with this person for two songs. If you want to make the followers' logic more complicated than "yes unless it has to be no", you're welcome to do so, but it will make the project harder.

Requirements

•   each person must be an individual thread.

•   another "main" thread might be necessary to announce (print) the results at the end.

•   everybody communicates with each other through some notion of a mailbox: each person has a dedicated resource (like a buffer) that anyone can attempt to send a message to, and only that person can read from. Only one message can be pending at a time, though.

◦                             these communication resources must all be collectively accessible at the same time as each other, butindividually accessible by only one person at a time.

•   two command line arguments, integers representing N and then M, must be provided on the command line.

•   Leaders will be numbered from 1 to N. Followers will be numbered from 1 toM.

•   results will be presented in this exact fashion: "Leader X:" on the first line, followed by the eight dances and the follower (or "——" for unassigned) listed afterwards in a second column. By listing all leaders' dance cards, we have enough information.

Leader 1:

Waltz      with 3

Tango      with 5

Foxtrot    with 2

Quickstep  with 1

Rumba      ------

Samba      with 2

Cha Cha    with 3

Jive       ------

 

Leader 2:

Waltz      with 2

Tango      with 4

...

You do not have to find a "perfect" matching for all participants; some may end up with no available dances, even though others are still not dancing all dances. Perhaps the wrong dance is available, or they've already agreed to two other dances with each other, or N andM may be different values.

The Writeup

Because we are double-implementing a task, it is not supposed to be a particularly large task. As a last step, you will write up a brief summary of what you experienced (as a part of theREADME.txt file). There's no page limit, just make sure you've focused on each of the questions below. Perhaps a page and a half or two would be sufficient. Some questions that need to be answered here are:

•   For your programming task, what were the challenges that you faced? Where was there competition for resources, and where was there a need for cooperation?

•   In your imperative implementation, what aspects of the task were straightforward, and which ones felt laborious?

•   What kinds of bugs did you run into for the imperative implementation –deadlock? How did you attempt to inspect what was going wrong with the code? (Did you use any debuggers or anything? You weren't required to do so, I'm just curious how your experience went).

•   In your Haskell implementation, what aspects of the task were straightforward, and which ones felt laborious?

•   Again, what kinds of bugs arose during development, and how did you handle them?

•   Lastly, what piece of advice do you wish you had received at the start of the assignment?

Grading

Your score will be based on calculating consistent outcomes; for instance, no person can dance with multiple other people during the same dance. Also, if a lead and follow were both idle during a song they could have danced together, it isn't a consistent result. We will also be manually checking for the following aspects to your solution:

•   actual use of separate threads where appropriate.

•   no "global lock" : While one person is dealing with a request, all uninvolved people can be busily working on their own dance cards.

•   your code doesn't devolve to some predictable ordering. If you find some pattern, for instance if leader 1 always dances with follows 1, 1, 2, 2, 3, 3, 4, 4, and leader 2 always dances with partners 2, 2, 3, 3, 4, 4, 5, 5, then we need to shift something to make sure the outcomes can vary from run to run.

Scoring Breakdown

•   60%: consistent outcomes (split evenly between the two implementations)

•   25%: adhering to the

•   15%: completed writeup with reasonable content.


==================================================================================================

我的实现

DanceParty:

package mydance;

import java.util.Scanner;

public class DanceParty {
	public DanceType[] danceTypes = new DanceType[] {
			new DanceType(0, "Waltz"),
			new DanceType(1, "Tango"),
			new DanceType(2, "Foxtrot"),
			new DanceType(3, "Quickstep"),
			new DanceType(4, "Rumba"),
			new DanceType(5, "Samba"),
			new DanceType(6, "ChaCha"),
			new DanceType(7, "Jive")
	};
	public Leader[] leaders;
	public Follower[] followers;

	public static void main(String[] args) {
		DanceParty danceParty = new DanceParty();

		Scanner scanner = new Scanner(System.in);
		System.out.println("Welcome to this Dancing Party");
		System.out.println("Please Input Leader number:");
		int leaderNum = scanner.nextInt();
		System.out.println("Please Input Follower number:");
		int followerNum = scanner.nextInt();

		danceParty.init(leaderNum, followerNum);
		danceParty.start();
		danceParty.waitLeaders();
		danceParty.printResult();
	}

	

	
	public void init(int leaderNum, int followerNum) {
		this.leaders = new Leader[leaderNum];
		for (int i = 0; i < this.leaders.length; i++) {
			this.leaders[i] = new Leader(i);
			this.leaders[i].init(this);
		}
		this.followers = new Follower[followerNum];
		for (int i = 0; i < this.followers.length; i++) {
			this.followers[i] = new Follower(i);
			this.followers[i].init(this);
		}
	}

	public void start() {
		for (int i = 0; i < this.followers.length; i++) {
			this.followers[i].start();
		}
		for (int i = 0; i < this.leaders.length; i++) {
			this.leaders[i].start();
		}
	}
	
	public void waitLeaders() {
		boolean iscompleted = false;
		while (!iscompleted) {
			iscompleted = true;
			for (int i = 0; i < this.leaders.length; i++) {
				if (!this.leaders[i].isCompleted()) {
					iscompleted = false;
					break;
				}
			}
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) { 
				e.printStackTrace();
			}
		}
	}

	
	private void printResult() {
		for (int i = 0; i < this.leaders.length; i++) {
			Leader leader = this.leaders[i];
			System.out.println("Leader :" + i);
			for (int j = 0; j < leader.danceConfirmed.length; j++) {
				int followerId = leader.danceConfirmed[j];
				if (followerId < 0) {
					System.out.println(String.format("%15s with %3s", this.danceTypes[j].name, "--"));
				}
				else {
					System.out.println(String.format("%15s with %3s", this.danceTypes[j].name, followerId));
				}
			}
			System.out.println();
		}
	}
}


DanceType:

package mydance;

public class DanceType {
	public int id;
	public String name;

	public DanceType(int id, String name) {
		this.id = id;
		this.name = name;
	}

	@Override
	public String toString() { 
		return String.format("<dance:%s>", this.id);
	}
}

Follower:

package mydance;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

public class Follower {
	public static int Max_SameLeader_Dance_Count = 2;

	public int id;
	private DanceParty danceParty;

	// 受到的邀请
	private Queue<Invitation> invitations;
	//索引是danceType,值是leaderId
	private int[] confirmedDance;
	//索引是leaderId,值是已经match过的次数count
	private int[] leaderDance;

	public Follower(int id) {
		this.id = id;
	}

	public void init(DanceParty danceParty) {
		this.danceParty = danceParty;
		this.invitations = new LinkedBlockingQueue<Invitation>();
		this.confirmedDance = new int[danceParty.danceTypes.length];
		for (int i = 0; i < this.confirmedDance.length; i++) {
			this.confirmedDance[i] = -1;
		}
		this.leaderDance = new int[danceParty.leaders.length];
		for (int i = 0; i < this.leaderDance.length; i++) {
			this.leaderDance[i] = 0;
		}
	}

	private Thread _thread = new Thread() {
		@Override
		public void run() {
			while (true) {
				while (true) {
					Invitation inv = null;
					synchronized (Follower.this.invitations) {
						inv = Follower.this.invitations.poll();
					}
					if (inv != null) {
						System.out.println(String.format("%s收到邀请%s", Follower.this, inv));
						Follower.this.reply(inv);
					}
					else {
						break;
					}
				}
				try {
					synchronized (Follower.this.invitations) {
						Follower.this.invitations.wait();
					}
				} catch (InterruptedException e) { 
					e.printStackTrace();
				}
			}
		}
	};

	private void reply(Invitation inv) {
		synchronized (inv) {
			if (this.confirmedDance[inv.danceTypeId] >= 0) {
				inv.result = Invitation.Result_Reject;
				System.out.println(String.format("%s拒绝邀请,因为已和%s参与过舞蹈%s", this, danceParty.leaders[this.confirmedDance[inv.danceTypeId]], danceParty.danceTypes[inv.danceTypeId]));
			}
			else if (this.leaderDance[inv.leaderId] >= Max_SameLeader_Dance_Count) {
				inv.result = Invitation.Result_Reject;
				System.out.println(String.format("%s拒绝邀请,因为已接受过%s的%s次邀请", this, danceParty.leaders[inv.leaderId], Max_SameLeader_Dance_Count));
			}
			else {
				inv.result = Invitation.Result_Accept;
				this.confirmedDance[inv.danceTypeId] = inv.leaderId;
				this.leaderDance[inv.leaderId]++;
				System.out.println(String.format("%s接受邀请%s", this, inv));
			}
			inv.notify();
		}
	}

	public void addInvitation(Invitation invitation) {
		synchronized (this.invitations) {
			this.invitations.add(invitation);
			this.invitations.notify();
		}
	}

	@Override
	public String toString() {
		return String.format("<follower:%s>", this.id);
	}

	public void start() {
		this._thread.start();
	}

}

Invitation:

package mydance;

public class Invitation {

	public static int Result_Init = -1;
	public static int Result_Reject = 0;
	public static int Result_Accept = 1;

	public int leaderId;
	public int followerId;
	public int danceTypeId;
	public int result = Result_Init;

	public Invitation(int leaderId, int followerId, int danceTypeId) {
		this.leaderId = leaderId;
		this.followerId = followerId;
		this.danceTypeId = danceTypeId;
	}

	@Override
	public String toString() {
		return String.format("<invitation: %s, %s, %s>", this.leaderId, this.followerId, this.danceTypeId);
	}
}


Leader:

package mydance;

import java.lang.Thread.State;
import java.util.Random;

public class Leader {
	public static int Invitation_Response_Wait_Timeout = 2000;

	public int id;
	private DanceParty danceParty;
	// 索引是danceType,值是followerId
	public int[] danceConfirmed;

	public Leader(int id) {
		this.id = id;
	}

	public void init(DanceParty danceParty) {
		this.danceParty = danceParty;
		this.danceConfirmed = new int[danceParty.danceTypes.length];
		for (int i = 0; i < this.danceConfirmed.length; i++) {
			this.danceConfirmed[i] = -1;
		}
	}

	private Thread _thread = new Thread() {
		@Override
		public void run() {
			Random random = new Random();
			// 随机选择舞蹈
			int[] dances = new int[Leader.this.danceConfirmed.length];
			for (int i = 0; i < dances.length; i++) {
				dances[i] = i;
			}
			for (int i = 0; i < dances.length; i++) {
				int randi = random.nextInt(dances.length - 1);
				int temp = dances[i];
				dances[i] = dances[randi];
				dances[randi] = temp;
			}
			for (int index = 0; index < dances.length; index++) {
				int danceTypeId = dances[index];
				if (Leader.this.danceConfirmed[danceTypeId] < 0) {
					// 随机抽取伴舞
					Follower[] followers = new Follower[danceParty.followers.length];
					for (int i = 0; i < followers.length; i++) {
						followers[i] = danceParty.followers[i];
					}
					for (int i = 0; i < followers.length; i++) {
						int randi = random.nextInt(followers.length - 1);
						Follower temp = followers[i];
						followers[i] = followers[randi];
						followers[randi] = temp;
					}

					for (Follower follower : followers) {
						Invitation inv = new Invitation(Leader.this.id, follower.id, danceTypeId);
						synchronized (inv) {
							try {
								Thread.sleep(random.nextInt(10));
								follower.addInvitation(inv);
								System.out.println(String.format("%s发送邀请%s...", Leader.this, inv));
								inv.wait(Invitation_Response_Wait_Timeout);
								if (inv.result == Invitation.Result_Init) {
									System.out.println(String.format("%s等待超时%s", Leader.this, inv));
								}
								else if (inv.result == Invitation.Result_Accept) {
									Leader.this.danceConfirmed[inv.danceTypeId] = inv.followerId;
									System.out.println(String.format("%s收到了接受的回应%s====", Leader.this, inv));
									break;
								}
								else if (inv.result == Invitation.Result_Reject) {
									System.out.println(String.format("%s收到了拒绝邀请的回应%s", Leader.this, inv));
								}
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
	};

	@Override
	public String toString() {
		return String.format("<leader:%s>", this.id);
	}

	public void start() {
		this._thread.start();
	}

	public boolean isCompleted() {
		State state = this._thread.getState();
		return state == State.TERMINATED;
	}
}


待改善的地方:

1. 发送邀请后遭到已进行2次共舞的拒绝后,可不再发送邀请
2. 挑选舞伴的算法可以继续优化,使得各个follower均匀参与
3. 对于4,4的输入,本可以有全部选中的结果

1. 目的: 调试、修改、运行模拟程序,通过形象化的状态显示,使学生理解进程的概念,了解同步和通信的过程,掌握进程通信和同步的机制,特别是利用缓冲区进行同步和通信的过程。通过补充新功能,使学生能灵活运用相关知识,培养创新能力。 2. 内容及要求: 1) 调试、运行模拟程序。 2) 发现并修改程序中不完善的地方。 3) 修改程序,使用随机数控制创建生产者消费者的过程。 4) 在原来程序的基础上,加入缓冲区的写互斥控制功能,模拟多个进程存取一个公共缓冲区,当有进程正在写缓冲区时,其他要访问该缓冲区的进程必须等待,当有进程正在读取缓冲区时,其他要求读取的进程可以访问,而要求写的进程应该等待。 5) 完成1)、2)、3)功能的,得基本分,完成4)功能的加2分,有其它功能改进的再加2分 3. 程序说明:   本程序是模拟两个进程,生产者(producer)和消费者(Consumer)工作。生产者每次产生一个数据,送入缓冲区中。消费者每次从缓冲区中取走一个数据。缓冲区可以容纳8个数据。因为缓冲区是有限的,因此当其满了时生产者进程应该等待,而空时,消费者进程应该等待;当生产者向缓冲区放入了一个数据,应唤醒正在等待的消费者进程,同样,当消费者取走一个数据后,应唤醒正在等待的生产者进程。就是生产者消费者之间的同步。   每次写入和读出数据时,都将读和写指针加一。当读写指针同样时,又一起退回起点。当写指针指向最后时,生产者就等待。当读指针为零时,再次要读取的消费者也应该等待。 为简单起见,每次产生的数据为0-99的整数,从0开始,顺序递增。两个进程的调度是通过运行者使用键盘来实现的。 4. 程序使用的数据结构 进程控制块:包括进程名,进程状态和执行次数。 缓冲区:一个整数数组。 缓冲区说明块:包括类型,读指针,写指针,读等待指针和写等待指针。 5. 程序使用说明   启动程序后,如果使用'p'键则运行一次生产者进程,使用'c'键则运行一次消费者进程。通过屏幕可以观察到两个进程的状态和缓冲区变化的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值